Tridora-CPU/tridoracpu/tridoracpu.srcs/vgafb.v

294 lines
11 KiB
Verilog

`timescale 1ns / 1ps
`default_nettype none
// Project F: Display Timings
// (C)2019 Will Green, Open Source Hardware released under the MIT License
// Learn more at https://projectf.io
//128K video memory is not enough for 640x480x4
`define RES_640_400
//`define RES_1024_768
module display_timings #(
H_RES=640, // horizontal resolution (pixels)
V_RES=480, // vertical resolution (lines)
H_FP=16, // horizontal front porch
H_SYNC=96, // horizontal sync
H_BP=48, // horizontal back porch
V_FP=10, // vertical front porch
V_SYNC=2, // vertical sync
V_BP=33, // vertical back porch
H_POL=0, // horizontal sync polarity (0:neg, 1:pos)
V_POL=0 // vertical sync polarity (0:neg, 1:pos)
)
(
input wire i_pix_clk, // pixel clock
input wire i_rst, // reset: restarts frame (active high)
output wire o_hs, // horizontal sync
output wire o_vs, // vertical sync
output wire o_de, // display enable: high during active video
output wire o_frame, // high for one tick at the start of each frame
output wire o_scanline, // high for one tick at the start of each scanline
output reg o_vblank, // high during vertical blank phase
output reg signed [15:0] o_sx, // horizontal beam position (including blanking)
output reg signed [15:0] o_sy // vertical beam position (including blanking)
);
// Horizontal: sync, active, and pixels
localparam signed H_STA = 0 - H_FP - H_SYNC - H_BP; // horizontal start
localparam signed HS_STA = H_STA + H_FP; // sync start
localparam signed HS_END = HS_STA + H_SYNC; // sync end
localparam signed HA_STA = 0; // active start
localparam signed HA_END = H_RES - 1; // active end
// Vertical: sync, active, and pixels
localparam signed V_STA = 0 - V_FP - V_SYNC - V_BP; // vertical start
localparam signed VS_STA = V_STA + V_FP; // sync start
localparam signed VS_END = VS_STA + V_SYNC; // sync end
localparam signed VA_STA = 0; // active start
localparam signed VA_END = V_RES - 1; // active end
// generate sync signals with correct polarity
assign o_hs = H_POL ? (o_sx > HS_STA && o_sx <= HS_END)
: ~(o_sx > HS_STA && o_sx <= HS_END);
assign o_vs = V_POL ? (o_sy > VS_STA && o_sy <= VS_END)
: ~(o_sy > VS_STA && o_sy <= VS_END);
// display enable: high during active period
assign o_de = (o_sx >= 0 && o_sy >= 0);
// o_frame: high for one tick at the start of each frame
assign o_frame = (o_sy == V_STA && o_sx == H_STA);
// o_scanline: high for one tick at the start of each visible scanline
assign o_scanline = (o_sy >= VA_STA) && (o_sy <= VA_END) && (o_sx == H_STA);
// set vblank at end of frame, clear at start
always @(posedge i_pix_clk)
begin
if(o_sy == VA_END) o_vblank <= 1;
else if (o_sy == -1) o_vblank <= 0;
end
always @ (posedge i_pix_clk)
begin
if (i_rst) // reset to start of frame
begin
o_sx <= H_STA;
o_sy <= V_STA;
end
else
begin
if (o_sx == HA_END) // end of line
begin
o_sx <= H_STA;
if (o_sy == VA_END) // end of frame
o_sy <= V_STA;
else
o_sy <= o_sy + 16'sh1;
end
else
o_sx <= o_sx + 16'sh1;
end
end
endmodule
// Project F: Display Controller VGA Demo
// (C)2020 Will Green, Open source hardware released under the MIT License
// Learn more at https://projectf.io
// This demo requires the following Verilog modules:
// * display_clocks
// * display_timings
// * test_card_simple or another test card
module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
input wire cpu_clk, // cpu clock
input wire CLK, // pixel clock
input wire RST_BTN, // reset button
input wire[3:0] reg_sel, // register select/address
output wire [VMEM_DATA_WIDTH-1:0] rd_data,
input wire [VMEM_DATA_WIDTH-1:0] wr_data,
input wire rd_en,
input wire wr_en,
output wire VGA_HS, // horizontal sync output
output wire VGA_VS, // vertical sync output
output wire [3:0] VGA_R, // 4-bit VGA red output
output wire [3:0] VGA_G, // 4-bit VGA green output
output wire [3:0] VGA_B // 4-bit VGA blue output
);
localparam BITS_PER_PIXEL = 4; localparam MAX_SHIFT_COUNT = 7;
localparam REG_RD_ADDR = 0; localparam REG_WR_ADDR = 1; localparam REG_VMEM = 2;
localparam REG_PAL_SLOT = 3; localparam REG_PAL_DATA = 4;
localparam REG_CTL = 5;
localparam COLOR_WIDTH = 12;
localparam PALETTE_WIDTH = 4;
// Display Clocks
wire pix_clk = CLK; // pixel clock
wire clk_lock = 1; // clock locked?
wire vmem_rd_en;
wire vmem_wr_en;
wire [VMEM_DATA_WIDTH-1:0] vmem_rd_data;
reg [VMEM_ADDR_WIDTH-1:0] cpu_rd_addr;
reg [VMEM_ADDR_WIDTH-1:0] cpu_wr_addr;
reg [VMEM_ADDR_WIDTH-1:0] pix_addr;
wire [VMEM_DATA_WIDTH-1:0] pix_data;
wire pix_rd;
wire [VMEM_DATA_WIDTH-1:0] status;
assign vmem_rd_en = rd_en;
assign vmem_wr_en = (reg_sel == REG_VMEM) && wr_en;
assign rd_data = (reg_sel == REG_VMEM) ? vmem_rd_data :
(reg_sel == REG_RD_ADDR) ? cpu_rd_addr :
(reg_sel == REG_WR_ADDR) ? cpu_wr_addr :
(reg_sel == REG_CTL) ? status :
32'hFFFFFFFF;
wire [VMEM_ADDR_WIDTH-1:0] cpu_addr = vmem_wr_en ? cpu_wr_addr : cpu_rd_addr;
bram_tdp #(.DATA(VMEM_DATA_WIDTH),.ADDR(VMEM_ADDR_WIDTH)) vram0 (
.a_rd(vmem_rd_en), .a_clk(cpu_clk),
.a_wr(vmem_wr_en), .a_addr(cpu_addr), .a_din(wr_data),
.a_dout(vmem_rd_data),
.b_clk(pix_clk), .b_addr(pix_addr), .b_dout(pix_data),
.b_rd(pix_rd)
);
wire palette_wr_en = (reg_sel == REG_PAL_DATA) && wr_en;
reg [PALETTE_WIDTH-1:0] palette_wr_slot;
wire [COLOR_WIDTH-1:0] color_data;
palette palette0(.wr_clk(cpu_clk), .rd_clk(pix_clk), .wr_en(palette_wr_en),
.wr_slot(palette_wr_slot), .wr_data(wr_data[COLOR_WIDTH-1:0]),
.rd_slot(pixel[3:0]), .rd_data(color_data));
// Display Timings
wire signed [15:0] sx; // horizontal screen position (signed)
wire signed [15:0] sy; // vertical screen position (signed)
wire h_sync; // horizontal sync
wire v_sync; // vertical sync
wire de; // display enable
wire frame; // frame start
wire scanline; // scanline start
wire vblank; // vertical blank
reg vblank_buf; // vertical blank in cpu clock domain
reg vblank_xfer; // vertical blank clock domain crossing
display_timings #( // 640x480 800x600 1280x720 1920x1080
`ifdef RES_1024_768
.H_RES(1024), // 640 800 1280 1920
.V_RES(768), // 480 600 720 1080
.H_FP(24), // 16 40 110 88
.H_SYNC(136), // 96 128 40 44
.H_BP(160), // 48 88 220 148
.V_FP(3), // 10 1 5 4
.V_SYNC(6), // 2 4 5 5
.V_BP(29), // 33 23 20 36
.H_POL(0), // 0 1 1 1
.V_POL(0) // 0 1 1 1
`endif
`ifdef RES_640_400
.H_RES(640),
.V_RES(400),
.H_FP(16),
.H_SYNC(96),
.H_BP(48),
.V_FP(12),
.V_SYNC(2),
.V_BP(35),
.H_POL(0),
.V_POL(1)
`endif
)
display_timings_inst (
.i_pix_clk(CLK),
.i_rst(!RST_BTN),
.o_hs(h_sync),
.o_vs(v_sync),
.o_de(de),
.o_frame(frame),
.o_scanline(scanline),
.o_vblank(vblank),
.o_sx(sx),
.o_sy(sy)
);
wire [7:0] red;
wire [7:0] green;
wire [7:0] blue;
reg [VMEM_DATA_WIDTH-1:0] shifter;
reg [4:0] shift_count;
// delayed frame signal for pix_rd
reg frame_d;
assign pix_rd = frame_d || scanline || (shift_count == 2);
assign status = { 4'b0001, {(VMEM_DATA_WIDTH-5){1'b0}}, vblank_buf};
wire [BITS_PER_PIXEL-1:0] pixel = shifter[VMEM_DATA_WIDTH-1:VMEM_DATA_WIDTH-BITS_PER_PIXEL];
always @(posedge pix_clk) frame_d <= frame;
always @(posedge cpu_clk) { vblank_buf, vblank_xfer } <= { vblank_xfer, vblank };
always @(posedge cpu_clk)
begin
if(wr_en)
begin
case(reg_sel)
REG_RD_ADDR: cpu_rd_addr <= wr_data;
REG_WR_ADDR: cpu_wr_addr <= wr_data;
REG_VMEM: cpu_wr_addr <= cpu_wr_addr + 1; // auto-increment write addr on write
REG_PAL_SLOT: palette_wr_slot <= wr_data[3:0];
endcase
end
else
if(rd_en && reg_sel == REG_VMEM) cpu_rd_addr <= cpu_rd_addr + 1; // auto-increment read addr on read
end
always @(posedge pix_clk)
begin
if(scanline || shift_count == MAX_SHIFT_COUNT) // before start of a line
begin // or at the end of a word, reset shifter with pixel data
shift_count <= 0;
shifter <= pix_data;
end
else if(de)
begin
shift_count <= shift_count + 1;
shifter <= shifter << BITS_PER_PIXEL;
end
if(frame) // at start of frame, reset pixel pointer
pix_addr <= 0;
else if(shift_count == 1) // after the first pixel, increment address
pix_addr <= pix_addr + 1;
end
// Hard-Coded RGBI palette
// // Pixel = { red, green, blue, intensity }
// assign red = pixel[3] ? pixel[0] ? 255 : 127: 0;
// assign green = pixel[2] ? pixel[0] ? 255 : 127: 0;
// assign blue = pixel[1] ? pixel[0] ? 255 : 127: 0;
// // VGA Output
// // VGA Pmod is 12-bit so we take the upper nibble of each colour
// assign VGA_HS = h_sync;
// assign VGA_VS = v_sync;
// assign VGA_R = de ? red[7:4] : 4'b0;
// assign VGA_G = de ? green[7:4] : 4'b0;
// assign VGA_B = de ? blue[7:4] : 4'b0;
// 12 bit RGB palette
assign VGA_HS = h_sync;
assign VGA_VS = v_sync;
assign VGA_R = de ? color_data[11:8] : 4'b0;
assign VGA_G = de ? color_data[7:4] : 4'b0;
assign VGA_B = de ? color_data[3:0] : 4'b0;
endmodule