Skip to content

Instantly share code, notes, and snippets.

@jfoucher
Created February 18, 2021 17:20
Show Gist options
  • Save jfoucher/7915b8f64bdc7e66668144c67ccfdd85 to your computer and use it in GitHub Desktop.
Save jfoucher/7915b8f64bdc7e66668144c67ccfdd85 to your computer and use it in GitHub Desktop.
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
/**
* 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
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
//------------------------------------------------------------------
//-- 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