Created
February 18, 2021 17:20
-
-
Save jfoucher/7915b8f64bdc7e66668144c67ccfdd85 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module display_timings ( | |
input wire clk_pix, // pixel clock | |
input wire rst, // reset | |
output reg [9:0] sx, // horizontal screen position | |
output reg [9:0] sy, // vertical screen position | |
output wire hsync, // horizontal sync | |
output wire vsync, // vertical sync | |
output wire de // data enable (low in blanking interval) | |
); | |
initial begin | |
sx = 0; | |
sy = 0; | |
end | |
// horizontal timings | |
parameter HA_END = 639; // end of active pixels | |
parameter HS_STA = HA_END + 16; // sync starts after front porch | |
parameter HS_END = HS_STA + 96; // sync ends | |
parameter LINE = 799; // last pixel on line (after back porch) | |
// vertical timings | |
parameter VA_END = 479; // end of active pixels | |
parameter VS_STA = VA_END + 10; // sync starts after front porch | |
parameter VS_END = VS_STA + 2; // sync ends | |
parameter SCREEN = 524; // last line on screen (after back porch) | |
assign hsync = ~(sx >= HS_STA && sx < HS_END); // invert: negative polarity | |
assign vsync = ~(sy >= VS_STA && sy < VS_END); // invert: negative polarity | |
assign de = (sx <= HA_END && sy <= VA_END); | |
// calculate horizontal and vertical screen position | |
always @(posedge clk_pix) begin | |
if (sx == LINE) begin // last pixel on line? | |
sx <= 0; | |
sy <= (sy == SCREEN) ? 0 : sy + 1; // last line on screen? | |
end else begin | |
sx <= sx + 1; | |
end | |
if (rst) begin | |
sx <= 0; | |
sy <= 0; | |
end | |
end | |
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* PLL configuration | |
* | |
* This Verilog module was generated automatically | |
* using the icepll tool from the IceStorm project. | |
* Use at your own risk. | |
* | |
* Given input frequency: 12.000 MHz | |
* Requested output frequency: 100.400 MHz | |
* Achieved output frequency: 100.500 MHz | |
*/ | |
module pll( | |
input clock_in, | |
input reset, | |
output clock_out, | |
output locked | |
); | |
SB_PLL40_CORE #( | |
.FEEDBACK_PATH("SIMPLE"), | |
.DIVR(4'b0000), // DIVR = 0 | |
.DIVF(7'b1000010), // DIVF = 66 | |
.DIVQ(3'b011), // DIVQ = 3 | |
.FILTER_RANGE(3'b001) // FILTER_RANGE = 1 | |
) uut ( | |
.LOCK(locked), | |
.RESETB(reset), | |
.BYPASS(1'b0), | |
.REFERENCECLK(clock_in), | |
.PLLOUTCORE(clock_out) | |
); | |
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
set_io HSYNC 48 | |
set_io VSYNC 3 | |
set_io CLK_12M 20 | |
set_io REG[0] 36 | |
set_io REG[1] 42 | |
set_io REG[2] 38 | |
set_io DATA[0] 43 | |
set_io DATA[1] 34 | |
set_io DATA[2] 37 | |
set_io DATA[3] 31 | |
set_io DATA[4] 28 | |
set_io DATA[5] 32 | |
set_io DATA[6] 27 | |
set_io DATA[7] 26 | |
#set_io DO 47 | |
set_io PHI2 45 | |
set_io RW 25 | |
set_io EN 23 | |
set_io OE 21 | |
set_io RGB[7] 13 | |
set_io RGB[6] 19 | |
set_io RGB[5] 18 | |
set_io RGB[4] 11 | |
set_io RGB[3] 9 | |
set_io RGB[2] 6 | |
set_io RGB[1] 44 | |
set_io RGB[0] 4 | |
set_io -nowarn led[0] 41 | |
set_io -nowarn led[1] 39 | |
set_io -nowarn led[2] 40 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------------------------------ | |
//-- Hello world example | |
//-- Turn on all the leds | |
//-- This example has been tested on the following boards: | |
//-- * Lattice icestick | |
//-- * Icezum alhambra (https://github.com/FPGAwars/icezum) | |
//------------------------------------------------------------------ | |
module vga ( | |
output VSYNC, | |
output HSYNC, | |
output OE, | |
output [7:0] RGB, | |
output reg [2:0] led, | |
input CLK_12M, | |
input RW, | |
input EN, | |
input PHI2, | |
input [2:0] REG, | |
input [7:0] DATA | |
); | |
assign OE = 1; | |
`define CTRL_REG 3'd0 // Formatted as follows |INCR_5|INCR_4|INCR_3|INCR_2|INCR_1|INCR_0|MODE_1|MODE_0| default to LORES | |
`define ADDR_LOW_REG 3'd1 // also contains the increment ||||ADDR4|ADDR_3|ADDR_2|ADDR_1|ADDR_0| | |
`define ADDR_HIGH_REG 3'd2 | |
`define DATA_REG 3'd3 | |
`define IEN_REG 3'd4 // formatted as follows |VSYNC| | | | | | |HSYNC| | |
`define INTR_REG 3'd5 // formatted as follows |VSYNC| | | | | | |HSYNC| | |
`define HSCROLL_REG 3'd6 // Scrolls one column in character mode and one pixel in pixel mode - negative is left, positive is right | |
`define VSCROLL_REG 3'd7 // Scrolls one line in character mode and one pixel in pixel mode - negative is down, positive is up | |
reg [7:0] ctrl_reg; | |
reg [7:0] ien_reg; | |
reg [7:0] intr_reg; | |
reg [4:0] increment_reg; | |
reg increment_neg; | |
reg [7:0] hscroll_reg; | |
reg [7:0] vscroll_reg; | |
reg [12:0] address_reg; | |
wire [7:0] increment = (increment_reg == 0) ? 0 : (increment_reg <= 8 ? (1 << (increment_reg-1)) : | |
(increment_reg == 9 ? 3 : | |
(increment_reg == 10 ? 10 : | |
(increment_reg == 11 ? H_CHAR_LORES : | |
(increment_reg == 12 ? H_CHAR_HIRES : | |
(increment_reg == 13 ? H_CHAR_HIRES*2 : | |
(increment_reg == 14 ? H_CHAR_LORES*3 : H_CHAR_HIRES*3 | |
))))))); | |
localparam H_RES_FULL = 800; | |
localparam V_RES_FULL = 525; | |
localparam H_RES = 640; | |
localparam V_RES = 480; | |
localparam V_CHAR_HIRES = 60; | |
localparam H_CHAR_HIRES = 80; | |
localparam V_CHAR_LORES = 30; | |
localparam H_CHAR_LORES = 40; | |
//assign DATA = ~EN ? (~RW ? 8'bzzzzzzzz : data_out) : 8'bzzzzzzzz; | |
reg [7:0] data_out = 0; | |
parameter MEM_INIT_FILE = "char_data.mem"; | |
reg [7:0] character_rom['h400-1:0]; | |
reg [7:0] character_ram['h400-1:0]; | |
reg [7:0] fb0['h1400-1:0]; | |
reg [7:0] fb1['h1400-1:0]; | |
reg [7:0] bgcolor = 8'hF4; | |
reg [7:0] fgcolor = 8'h03; | |
// integer i; | |
// initial begin | |
// address_reg <= 0; | |
// for (i = 0; i < 'h1400; i = i + 1) begin | |
// fb0[i] <= 8'h20; | |
// //fb1[i] <= 8'h20; | |
// end | |
// end | |
reg RESET; | |
initial begin | |
RESET <= 1; | |
if (MEM_INIT_FILE != "") begin | |
$readmemh(MEM_INIT_FILE, character_rom); | |
//$readmemh(MEM_INIT_FILE, character_ram); | |
end | |
end | |
initial begin | |
CLK_25M <= 1'b0; | |
RESET <= 1'b0; | |
end | |
wire CLK; | |
wire enabled = ~EN; | |
wire write = ~RW; | |
wire CLK_100M; | |
reg CLK_25M; | |
wire locked; | |
reg [2:0] red; | |
reg [2:0] green; | |
reg [1:0] blue; | |
assign RGB[7:5] = red; | |
assign RGB[4:2] = green; | |
assign RGB[1:0] = blue; | |
pll mypll(.clock_in(CLK_12M), .clock_out(CLK_100M), .locked(locked), .reset(~RESET)); | |
//////// | |
// make a simple blink circuit | |
//////// | |
// keep track of time and location in blink_pattern | |
reg fast_counter = 1'b0; | |
wire DE; | |
wire [9:0] XPOS; | |
wire [9:0] YPOS; | |
// increment the blink_counter every clock | |
always @(posedge CLK_100M) begin | |
fast_counter <= ~fast_counter; | |
if (fast_counter == 1'b1) begin | |
CLK_25M <= ~CLK_25M; | |
end | |
end | |
display_timings timings (.vsync(VSYNC), .hsync(HSYNC), .clk_pix(CLK_25M), .de(DE), .sx(XPOS), .sy(YPOS), .rst(RESET)); | |
// TRY WITH DOUBLE BUFFER INSTEAD OF FIFO | |
// Maybe with SPRAM | |
// Now it is true that for the ice40 BRAM there needs to be two clock cycles between setting the address and reading the byte, but we can already set the new address in the next cycle, allowing us to read a byte every clock cycle once we set the initial address. | |
// https://fpga.michelanders.nl/2020/02/ice40-bram-spram-access-need-for-speed.html | |
reg [2:0] command_reg; | |
reg [7:0] command_data; | |
//wire get_reg = (PHI2 == 1'b1) && (enabled == 1'b1) && (write == 1'b1); | |
always @(posedge CLK_100M) begin | |
if ((PHI2 == 1'b1) && (enabled == 1'b1) && (write == 1'b1)) begin | |
command_reg <= REG; | |
end | |
end | |
reg old_phi2; | |
reg save_data; | |
reg [1:0] save_state; | |
`define SAVE_STATE_INIT 2'd0 | |
`define SAVE_STATE_SAVE 2'd1 | |
`define SAVE_STATE_INCREMENT 2'd2 | |
`define SAVE_STATE_WAIT 2'd3 | |
reg [7:0] cur_char; | |
reg [7:0] fb0_char; | |
reg [7:0] fb1_char; | |
reg [7:0] ram_data; | |
reg get_state; | |
//wire [7:0] char_data_line = character_rom[((cur_char & 8'h7F) << 3) + (YPOS & 8'h7)]; | |
//wire [7:0] char_data_line = (cur_char < 8'h80) ? character_rom[((cur_char & 8'h7F) << 3) + (YPOS & 8'h7)] : (DE ? character_ram[((cur_char & 8'h7F) << 3) + (YPOS & 8'h7)] : 8'h0); | |
wire cur_px = char_data_line[(XPOS & 8'h7)]; | |
reg [7:0] char_data_line; | |
reg [7:0] ram_char; | |
reg [12:0] copy_addr; | |
reg current_fb; | |
wire [12:0] char_add = (YPOS >> 3)* H_CHAR_HIRES +(XPOS >> 3); | |
//wire [12:0] ram_add = DE ? char_add : copy_addr; | |
reg [12:0] ram_add; | |
//wire [12:0] ram_add = (YPOS >> 3)* H_CHAR_HIRES +(XPOS >> 3); | |
//wire save = (enabled == 1'b1) && (write == 1'b1) && (save_data == 1'b0); | |
always @(posedge CLK_100M) begin | |
//copy_addr <= 0; | |
ram_add <= (YPOS >> 3)* H_CHAR_HIRES +(XPOS >> 3); | |
fb1_char <= fb1[ram_add]; | |
fb0_char <= fb0[ram_add]; | |
old_phi2 <= PHI2; | |
if ((PHI2 == 1'b1) && (enabled == 1'b1) && (write == 1'b1) && (save_data == 1'b0)) begin | |
command_data <= DATA; | |
save_state <= `SAVE_STATE_INIT; | |
save_data <= 1'b0; | |
end | |
//led <= 3'b111; //off | |
else if ((old_phi2 == 1'b1) && (PHI2 == 1'b0) && (enabled == 1'b1) && (write == 1'b1) && (save_data == 1'b0)) begin | |
//command_data <= DATA; | |
save_state <= `SAVE_STATE_SAVE; | |
save_data <= 1'b1; | |
//led <= 3'b011; //blue | |
end | |
// else if ((YPOS > V_RES) && !save_data && !current_fb) begin | |
// fb0[copy_addr > 0 ? copy_addr : 0] <= fb1_char; | |
// copy_addr <= copy_addr+1; | |
// if(copy_addr >= 'h1400-1) begin | |
// copy_addr <= 1'b0; | |
// end | |
// end | |
// else if ((YPOS > V_RES) && !save_data && current_fb) begin | |
// fb1[copy_addr > 0 ? copy_addr : 0] <= fb0_char; | |
// copy_addr <= copy_addr+1; | |
// if(copy_addr >= 'h1400-1) begin | |
// copy_addr <= 1'b0; | |
// end | |
// end | |
else begin | |
case(save_state) | |
`SAVE_STATE_INIT: | |
begin | |
save_data <= 1'b0; | |
save_state <= `SAVE_STATE_INIT; | |
led <= 3'b111; | |
end | |
`SAVE_STATE_SAVE: | |
begin | |
led <= command_reg; | |
//led <= command_reg; //blue | |
case (command_reg) | |
`CTRL_REG: | |
begin | |
ctrl_reg <= command_data; | |
//led <= 3'b011; //blue | |
if (enabled == 1'b0) begin | |
save_data <= 1'b0; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
end | |
`DATA_REG: | |
begin | |
if (address_reg == 13'h1FFE) begin | |
bgcolor <= command_data; | |
end | |
else if(address_reg == 13'h1FFF) begin | |
fgcolor <= command_data; | |
end | |
else if(address_reg >= 13'h1400) begin | |
character_ram[address_reg - 13'h1400] <= command_data; | |
end | |
//else if (current_fb) begin | |
fb0[address_reg] <= command_data; | |
// end | |
// else if (!current_fb) begin | |
// fb1[address_reg] <= command_data; | |
// end | |
if (enabled == 1'b0) begin | |
led <= 3'b110; | |
save_state <= `SAVE_STATE_INCREMENT; | |
end | |
//led <= 3'b110; //red | |
end | |
`ADDR_LOW_REG: | |
begin | |
address_reg[4:0] <= command_data; | |
if (enabled == 1'b0) begin | |
save_data <= 1'b0; | |
led <= 3'b111; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
//led <= 3'b101; //green | |
end | |
`ADDR_HIGH_REG: | |
begin | |
address_reg[12:5] <= command_data; | |
if (enabled == 1'b0) begin | |
save_data <= 1'b0; | |
led <= 3'b111; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
//led <= 3'b100; //green | |
end | |
default: | |
begin | |
//led <= 3'b111; // off | |
if (enabled == 1'b0) begin | |
save_data <= 1'b0; | |
led <= 3'b111; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
end | |
endcase | |
end | |
`SAVE_STATE_INCREMENT: | |
begin | |
address_reg <= address_reg + 1; | |
if (address_reg >= 'h1400) begin | |
address_reg <= 0; | |
end | |
if (enabled == 1'b1) begin | |
led <= 3'b110; | |
save_state <= `SAVE_STATE_WAIT; | |
end | |
else begin | |
save_data <= 1'b0; | |
led <= 3'b111; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
end | |
`SAVE_STATE_WAIT: | |
begin | |
if (enabled == 1'b1) begin | |
led <= 3'b000; | |
save_state <= `SAVE_STATE_WAIT; | |
end | |
else begin | |
save_data <= 1'b0; | |
led <= 3'b111; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
end | |
default: | |
begin | |
save_data <= 1'b0; | |
save_state <= `SAVE_STATE_INIT; | |
end | |
endcase | |
end | |
end | |
always @(posedge CLK_100M) begin | |
if (DE) begin | |
cur_char <= /*current_fb ? fb1_char : */fb0_char; | |
char_data_line <= character_rom[((cur_char & 8'h7F) << 3) + (YPOS & 8'h7)]; | |
ram_char <= character_ram[((cur_char & 8'h7F) << 3) + (YPOS & 8'h7)]; | |
end | |
end | |
always @(posedge CLK_25M) begin | |
red = (DE == 1'b1) ? (cur_px ? fgcolor[7:5] : bgcolor[7:5]) : 3'h0; | |
green = (DE == 1'b1) ? (cur_px ? fgcolor[4:2] : bgcolor[4:2]) : 3'h0; | |
blue = (DE == 1'b1) ? (cur_px ? fgcolor[1:0] : bgcolor[1:0]) : 2'h0; | |
if ((YPOS == V_RES) && (XPOS == 1'b0)) begin | |
current_fb <= ~current_fb; | |
end | |
end | |
endmodule |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment