Merge pull request #9 from alangarf/vga_pipeline

Pipelined VGA module
This commit is contained in:
Alan Garfield 2018-02-06 00:38:05 +11:00 committed by GitHub
commit 1f4fe41a97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1095 additions and 874 deletions

View File

@ -53,6 +53,7 @@ $(BUILDDIR)/apple1.blif: $(SOURCEDIR)/apple1.v \
$(SOURCEDIR)/uart/async_tx_rx.v \
$(SOURCEDIR)/vga/vga.v \
$(SOURCEDIR)/vga/vram.v \
$(SOURCEDIR)/vga/font_rom.v \
$(SOURCEDIR)/ps2keyboard/ps2keyboard.v \
$(SOURCEDIR)/boards/ice40hx8k/clock_pll.v \
$(SOURCEDIR)/boards/ice40hx8k/apple1_hx8k.v

View File

@ -33,157 +33,11 @@ module apple1_tb;
initial begin
// force core_top.clk_div = 0;
// force core_top.cpu_clken = 0;
// force core_top.hard_reset = 0;
// force core_top.reset_cnt = 0;
// force core_top.my_cpu.arlet_cpu.AB = 0;
// force core_top.my_cpu.arlet_cpu.PC = 0;
// force core_top.my_cpu.arlet_cpu.ABL = 0;
// force core_top.my_cpu.arlet_cpu.ABH = 0;
// force core_top.my_cpu.arlet_cpu.DIHOLD = 0;
// force core_top.my_cpu.arlet_cpu.IRHOLD = 0;
// force core_top.my_cpu.arlet_cpu.IRHOLD_valid = 0;
// force core_top.my_cpu.arlet_cpu.C = 0;
// force core_top.my_cpu.arlet_cpu.Z = 0;
// force core_top.my_cpu.arlet_cpu.I = 0;
// force core_top.my_cpu.arlet_cpu.D = 0;
// force core_top.my_cpu.arlet_cpu.V = 0;
// force core_top.my_cpu.arlet_cpu.N = 0;
// force core_top.my_cpu.arlet_cpu.AI = 0;
// force core_top.my_cpu.arlet_cpu.BI = 0;
// force core_top.my_cpu.arlet_cpu.DO = 0;
// force core_top.my_cpu.arlet_cpu.WE = 0;
// force core_top.my_cpu.arlet_cpu.CI = 0;
// force core_top.my_cpu.arlet_cpu.NMI_edge = 0;
// force core_top.my_cpu.arlet_cpu.regsel = 0;
// force core_top.my_cpu.arlet_cpu.PC_inc = 0;
// force core_top.my_cpu.arlet_cpu.PC_temp = 0;
// force core_top.my_cpu.arlet_cpu.src_reg = 0;
// force core_top.my_cpu.arlet_cpu.dst_reg = 0;
// force core_top.my_cpu.arlet_cpu.index_y = 0;
// force core_top.my_cpu.arlet_cpu.load_reg = 0;
// force core_top.my_cpu.arlet_cpu.inc = 0;
// force core_top.my_cpu.arlet_cpu.write_back = 0;
// force core_top.my_cpu.arlet_cpu.load_only = 0;
// force core_top.my_cpu.arlet_cpu.store = 0;
// force core_top.my_cpu.arlet_cpu.adc_sbc = 0;
// force core_top.my_cpu.arlet_cpu.compare = 0;
// force core_top.my_cpu.arlet_cpu.shift = 0;
// force core_top.my_cpu.arlet_cpu.rotate = 0;
// force core_top.my_cpu.arlet_cpu.backwards = 0;
// force core_top.my_cpu.arlet_cpu.cond_true = 0;
// force core_top.my_cpu.arlet_cpu.cond_code = 0;
// force core_top.my_cpu.arlet_cpu.shift_right = 0;
// force core_top.my_cpu.arlet_cpu.alu_shift_right = 0;
// force core_top.my_cpu.arlet_cpu.op = 0;
// force core_top.my_cpu.arlet_cpu.alu_op = 0;
// force core_top.my_cpu.arlet_cpu.adc_bcd = 0;
// force core_top.my_cpu.arlet_cpu.adj_bcd = 0;
// force core_top.my_cpu.arlet_cpu.bit_ins = 0;
// force core_top.my_cpu.arlet_cpu.plp = 0;
// force core_top.my_cpu.arlet_cpu.php = 0;
// force core_top.my_cpu.arlet_cpu.clc = 0;
// force core_top.my_cpu.arlet_cpu.sed = 0;
// force core_top.my_cpu.arlet_cpu.cli = 0;
// force core_top.my_cpu.arlet_cpu.sei = 0;
// force core_top.my_cpu.arlet_cpu.clv = 0;
// force core_top.my_cpu.arlet_cpu.brk = 0;
// force core_top.my_cpu.arlet_cpu.res = 0;
// force core_top.my_cpu.arlet_cpu.write_register = 0;
// force core_top.my_cpu.arlet_cpu.ADJL = 0;
// force core_top.my_cpu.arlet_cpu.ADJH = 0;
// force core_top.my_cpu.arlet_cpu.NMI_1 = 0;
// force core_top.my_cpu.arlet_cpu.ALU.OUT = 0;
// force core_top.my_cpu.arlet_cpu.ALU.CO = 0;
// force core_top.my_cpu.arlet_cpu.ALU.N = 0;
// force core_top.my_cpu.arlet_cpu.ALU.HC = 0;
// force core_top.my_cpu.arlet_cpu.ALU.AI7 = 0;
// force core_top.my_cpu.arlet_cpu.ALU.BI7 = 0;
// force core_top.my_cpu.arlet_cpu.ALU.temp_logic = 0;
// force core_top.my_cpu.arlet_cpu.ALU.temp_BI = 0;
// force core_top.my_cpu.arlet_cpu.ALU.temp_l = 0;
// force core_top.my_cpu.arlet_cpu.ALU.temp_h = 0;
clk25 = 1'b0;
uart_rx = 1'b1;
rst_n = 1'b0;
#40 rst_n = 1'b1;
// release core_top.clk_div;
// release core_top.cpu_clken;
// release core_top.hard_reset;
// release core_top.reset_cnt;
// release core_top.my_cpu.arlet_cpu.AB;
// release core_top.my_cpu.arlet_cpu.PC;
// release core_top.my_cpu.arlet_cpu.ABL;
// release core_top.my_cpu.arlet_cpu.ABH;
// release core_top.my_cpu.arlet_cpu.DIHOLD;
// release core_top.my_cpu.arlet_cpu.IRHOLD;
// release core_top.my_cpu.arlet_cpu.IRHOLD_valid;
// release core_top.my_cpu.arlet_cpu.C;
// release core_top.my_cpu.arlet_cpu.Z;
// release core_top.my_cpu.arlet_cpu.I;
// release core_top.my_cpu.arlet_cpu.D;
// release core_top.my_cpu.arlet_cpu.V;
// release core_top.my_cpu.arlet_cpu.N;
// release core_top.my_cpu.arlet_cpu.AI;
// release core_top.my_cpu.arlet_cpu.BI;
// release core_top.my_cpu.arlet_cpu.DO;
// release core_top.my_cpu.arlet_cpu.WE;
// release core_top.my_cpu.arlet_cpu.CI;
// release core_top.my_cpu.arlet_cpu.NMI_edge;
// release core_top.my_cpu.arlet_cpu.regsel;
// release core_top.my_cpu.arlet_cpu.PC_inc;
// release core_top.my_cpu.arlet_cpu.PC_temp;
// release core_top.my_cpu.arlet_cpu.src_reg;
// release core_top.my_cpu.arlet_cpu.dst_reg;
// release core_top.my_cpu.arlet_cpu.index_y;
// release core_top.my_cpu.arlet_cpu.load_reg;
// release core_top.my_cpu.arlet_cpu.inc;
// release core_top.my_cpu.arlet_cpu.write_back;
// release core_top.my_cpu.arlet_cpu.load_only;
// release core_top.my_cpu.arlet_cpu.store;
// release core_top.my_cpu.arlet_cpu.adc_sbc;
// release core_top.my_cpu.arlet_cpu.compare;
// release core_top.my_cpu.arlet_cpu.shift;
// release core_top.my_cpu.arlet_cpu.rotate;
// release core_top.my_cpu.arlet_cpu.backwards;
// release core_top.my_cpu.arlet_cpu.cond_true;
// release core_top.my_cpu.arlet_cpu.cond_code;
// release core_top.my_cpu.arlet_cpu.shift_right;
// release core_top.my_cpu.arlet_cpu.alu_shift_right;
// release core_top.my_cpu.arlet_cpu.op;
// release core_top.my_cpu.arlet_cpu.alu_op;
// release core_top.my_cpu.arlet_cpu.adc_bcd;
// release core_top.my_cpu.arlet_cpu.adj_bcd;
// release core_top.my_cpu.arlet_cpu.bit_ins;
// release core_top.my_cpu.arlet_cpu.plp;
// release core_top.my_cpu.arlet_cpu.php;
// release core_top.my_cpu.arlet_cpu.clc;
// release core_top.my_cpu.arlet_cpu.sec;
// release core_top.my_cpu.arlet_cpu.cld;
// release core_top.my_cpu.arlet_cpu.sed;
// release core_top.my_cpu.arlet_cpu.sei;
// release core_top.my_cpu.arlet_cpu.clv;
// release core_top.my_cpu.arlet_cpu.brk;
// release core_top.my_cpu.arlet_cpu.res;
// release core_top.my_cpu.arlet_cpu.write_register;
// release core_top.my_cpu.arlet_cpu.ADJL;
// release core_top.my_cpu.arlet_cpu.ADJH;
// release core_top.my_cpu.arlet_cpu.NMI_1;
// release core_top.my_cpu.arlet_cpu.ALU.OUT;
// release core_top.my_cpu.arlet_cpu.ALU.CO;
// release core_top.my_cpu.arlet_cpu.ALU.N;
// release core_top.my_cpu.arlet_cpu.ALU.HC;
// release core_top.my_cpu.arlet_cpu.ALU.AI7;
// release core_top.my_cpu.arlet_cpu.ALU.BI7;
// release core_top.my_cpu.arlet_cpu.ALU.temp_logic;
// release core_top.my_cpu.arlet_cpu.ALU.temp_BI;
// release core_top.my_cpu.arlet_cpu.ALU.temp_l;
// release core_top.my_cpu.arlet_cpu.ALU.temp_h;
$display("Starting...");
$dumpfile("apple1_top_tb.vcd");
$dumpvars;

2
iverilog/run_vga_tb.sh Executable file
View File

@ -0,0 +1,2 @@
iverilog -DSIM -g2005 -s vga_tb -o vga_tb -c vga_files.txt
vvp vga_tb

4
iverilog/vga_files.txt Normal file
View File

@ -0,0 +1,4 @@
../rtl/vga/vga.v
../rtl/vga/vram.v
../rtl/vga/font_rom.v
vga_tb.v

95
iverilog/vga_tb.v Normal file
View File

@ -0,0 +1,95 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Top level test bench for apple1_top
//
// Author.....: Niels A. Moseley
// Date.......: 26-1-2018
//
`timescale 1ns/1ps
module vga_tb;
reg clk25, rst, address, w_en, blink_clken;
reg [7:0] din;
wire vga_h_sync, vga_v_sync, vga_red, vga_grn, vga_blu;
//////////////////////////////////////////////////////////////////////////
// Setup dumping of data for inspection
initial begin
clk25 = 1'b0;
rst = 1'b0;
address = 1'b0;
w_en = 1'b0;
blink_clken = 1'b0;
din = 8'd0;
#5
rst = 1'b1;
#5
rst = 1'b0;
$display("Starting...");
$dumpfile("vga_tb.vcd");
$dumpvars;
//#180000
//uart_rx = 1'b0;
//#400
//uart_rx = 1'b1;
//#400
//uart_rx = 1'b0;
//#400
//uart_rx = 1'b1;
//#800
//uart_rx = 1'b0;
//#1600
//uart_rx = 1'b1;
#50000000 $display("Stopping...");
$finish;
end
//////////////////////////////////////////////////////////////////////////
// Clock
always
#20 clk25 = !clk25;
//////////////////////////////////////////////////////////////////////////
// Core of system
vga my_vga (
.clk25(clk25),
.enable(1'b1),
.rst(rst),
.vga_h_sync(vga_h_sync),
.vga_v_sync(vga_v_sync),
.vga_red(vga_red),
.vga_grn(vga_grn),
.vga_blu(vga_blu),
.address(address),
.w_en(w_en),
.din(din),
.blink_clken(blink_clken)
);
endmodule

File diff suppressed because it is too large Load Diff

View File

@ -958,3 +958,67 @@
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000

View File

@ -58,12 +58,10 @@ module apple1(
// Clocks
wire cpu_clken;
wire blink_clken;
clock my_clock(
.clk25(clk25),
.rst_n(rst_n),
.cpu_clken(cpu_clken),
.blink_clken(blink_clken)
.cpu_clken(cpu_clken)
);
//////////////////////////////////////////////////////////////////////////
@ -90,8 +88,8 @@ module apple1(
.we (we),
.irq_n (1'b1),
.nmi_n (1'b1),
.ready (cpu_clken)
//.pc_monitor (pc_monitor)
.ready (cpu_clken),
.pc_monitor (pc_monitor)
);
//////////////////////////////////////////////////////////////////////////
@ -196,11 +194,7 @@ module apple1(
.address(ab[0]),
.w_en(we & vga_cs),
.din(dbo),
.clr_screen_btn(clr_screen_btn),
.blink_clken(blink_clken),
.debug(pc_monitor)
.din(dbo)
);
//////////////////////////////////////////////////////////////////////////

View File

@ -28,8 +28,7 @@ module clock(
input rst_n, // active low synchronous reset
// Clock enables
output reg cpu_clken, // 1MHz clock enable for the CPU and devices
output reg blink_clken // 1Hz clock enable for cursor blink
output reg cpu_clken // 1MHz clock enable for the CPU and devices
);
// generate clock enable once every
@ -70,20 +69,4 @@ module clock(
end
`endif
reg [20:0] bclk_div;
always @(posedge clk25)
begin
// note: bclk_div should be compared to
// N-1, where N is the clock divisor
if (cpu_clken)
begin
if ((bclk_div == 500000) || (rst_n == 1'b0))
bclk_div <= 0;
else
bclk_div <= bclk_div + 1'b1;
blink_clken <= (bclk_div[20:0] == 0);
end
end
endmodule

67
rtl/vga/font_rom.v Normal file
View File

@ -0,0 +1,67 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: 8KB RAM for system
//
// Author.....: Alan Garfield
// Date.......: 3-2-2018
//
//`define DOTTY
//`define SCANLINES
`define NORMAL
module font_rom(
input clk, // clock signal
input [5:0] character, // address bus
input [3:0] pixel, // address of the pixel to output
input [4:0] line, // address of the line to output
output reg out // single pixel from address and pixel pos
);
`ifdef SIM
parameter ROM_FILENAME = "../roms/vga_font.bin";
`else
parameter ROM_FILENAME = "../../roms/vga_font.bin";
`endif
reg [7:0] rom[0:639];
initial
$readmemb(ROM_FILENAME, rom, 0, 639);
// double width of pixel by ignoring bit 0
wire [2:0] pixel_ptr;
assign pixel_ptr = (3'h7 - pixel[3:1]);
// double height of pixel by ignoring bit 0
wire [3:0] line_ptr = line[4:1];
always @(posedge clk)
begin
`ifdef DOTTY
out <= (line[0] & pixel[0]) ? rom[(character * 10) + {2'd0, line_ptr}][pixel_ptr] : 1'b0;
`endif
`ifdef SCANLINES
out <= (line[0]) ? rom[(character * 10) + {2'd0, line_ptr}][pixel_ptr] : 1'b0;
`endif
`ifdef NORMAL
out <= rom[(character * 10) + {2'd0, line_ptr}][pixel_ptr];
`endif
end
endmodule

View File

@ -9,301 +9,267 @@ module vga(
output vga_blu, // blue VGA signal
input address, // address bus
input w_en, // active high write enable strobe
input [7:0] din, // 8-bit data bas (input)
input clr_screen_btn, // active high clear screen button
input blink_clken, // cursor blink enable strobe
output [15:0] debug
input [7:0] din // 8-bit data bas (input)
);
reg [4:0] c_rom[0:447] /* synthesis syn_ramstyle = "block_ram" */;
initial begin
$readmemb("../../roms/vga_font.bin", c_rom, 0, 447);
end
reg [9:0] vram_r_addr;
reg [9:0] vram_w_addr;
reg vram_r_en;
reg vram_w_en;
reg [5:0] vram_din;
reg [5:0] vram_dout;
vram my_vram(
.clk(clk25),
.read_addr(vram_r_addr),
.write_addr(vram_w_addr),
.r_en(vram_r_en),
.w_en(vram_w_en),
.din(vram_din),
.dout(vram_dout)
);
//////////////////////////////////////////////////////////////////////////
// Registers and Parameters
// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 521; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter h_pixels = 799; // horizontal pixels per line
parameter v_lines = 520; // vertical lines per frame
parameter h_pulse = 96; // hsync pulse length
parameter v_pulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch
parameter hfp = 784; // beginning of horizontal front porch
parameter vbp = 31; // end of vertical back porch
parameter vfp = 511; // beginning of vertical front porch
// registers for storing the horizontal & vertical counters
reg [9:0] hc;
reg [9:0] vc;
reg [5:0] hpos;
reg [4:0] vpos;
reg [3:0] hdot;
reg [4:0] vdot;
reg [9:0] h_cnt;
reg [9:0] v_cnt;
wire [3:0] h_dot;
reg [4:0] v_dot;
// hardware cursor registers
wire [9:0] cursor;
reg [5:0] h_cursor;
reg [4:0] v_cursor;
reg [9:0] v_cursor;
wire vga_h_act;
wire vga_v_act;
// vram indexing registers
reg [9:0] vram_start_addr;
reg [5:0] vram_h_addr;
reg [9:0] vram_v_addr;
assign vga_h_act = (hc >= hbp && hc < hfp);
assign vga_v_act = (vc >= vbp && vc < vfp);
// vram registers
wire [9:0] vram_r_addr;
reg [9:0] vram_w_addr;
reg vram_w_en;
reg [5:0] vram_din;
wire [5:0] vram_dout;
assign vga_h_sync = (hc < hpulse) ? 0 : 1;
assign vga_v_sync = (vc < vpulse) ? 0 : 1;
//assign vblank = (vc >= vbp && vc < vfp) ? 0:1;
// font rom registers
wire [5:0] font_char;
wire [3:0] font_pixel;
wire [4:0] font_line;
wire font_out;
always @(posedge clk25)
begin
if (hc < hpixels - 1)
begin
hc <= hc + 1;
// count 16 pixels, so 640px / 16 = 40 characters
if (vga_h_act)
begin
hdot <= hdot + 1;
if (hdot == 4'hF)
begin
hdot <= 0;
hpos <= hpos + 1;
end
end
end
else
begin
// reset horizontal counters
hc <= 0;
hdot <= 0;
hpos <= 0;
if (vc < vlines - 1)
begin
vc <= vc + 1;
// count 20 rows, so 480px / 20 = 24 rows
if (vga_v_act)
begin
vdot <= vdot + 1;
if (vdot == 5'd19)
begin
vdot <= 0;
vpos <= vpos + 1;
end
end
end
else
begin
// reset vertical counters
vc <= 0;
vdot <= 0;
vpos <= 0;
end
end
end
reg out;
assign vga_red = out;
assign vga_grn = out;
assign vga_blu = out;
reg [8:0] cur_chr_offset;
reg [9:0] v_pos_offset;
reg [3:0] v_offset;
reg [2:0] h_offset;
reg blink;
always @(posedge clk25 or posedge rst)
begin
if (rst)
begin
vram_r_addr = 10'd0;
vram_r_en = 1'b0;
end
else
begin
// get the current character from vram and build
// offset to map into character ROM (5x7 font)
if (blink && (hpos == h_cursor && vpos == v_cursor))
cur_chr_offset = 9'd0; // the @ character
else
begin
vram_r_en = 1'b1;
v_pos_offset = (vpos * 40);
vram_r_addr = (v_pos_offset + {4'b0, hpos});
cur_chr_offset = (vram_dout * 7);
//cur_chr_offset <= (v_ram[hpos + (40 * vpos)] * 7);
end
case ({vga_h_act, vga_v_act})
default:
// outside display area
out = 1'b0;
2'b11:
begin
// we're inside the visible screen display frame
//
// scan doubling is achieved by ignoring bit 0 of both vdot
// and hdot counters, in affect doubling the pixel size
// (each pixel becomes screen pixels)
case (vdot[4:1])
4'b0000,
4'b0001,
4'b1001:
begin
// blank lines for spacing
out = 1'b0;
end
default:
begin
// work out character rom offset for current line
// taking away 2 from counter to allow for the two
// blank preceding lines
v_offset = (vdot[4:1] - 2);
case (hdot[3:1])
3'b000,
3'b110,
3'b111:
begin
// blank columns for spacing
out = 1'b0;
end
default:
begin
// work out the character rom offset for the current
// column. We reverse the dot pattern by subtracting
// the column from the number of pixel in the
// character row in rom
h_offset = (5 - hdot[3:1]);
// grab the pixel from the character rom for
// the given screen column and line
out = c_rom[cur_chr_offset + {5'b0, v_offset}][h_offset];
end
endcase
end
endcase
end
endcase
end
end
reg cls_flag, cls_running;
// cpu control registers
reg char_seen;
// active region strobes
wire h_active;
wire v_active;
assign h_active = (h_cnt >= hbp && h_cnt < hfp);
assign v_active = (v_cnt >= vbp && v_cnt < vfp);
//////////////////////////////////////////////////////////////////////////
// VGA Sync Generation
//
always @(posedge clk25 or posedge rst)
begin
if (rst)
begin
blink <= 1'b1;
h_cursor <= 6'd0;
v_cursor <= 5'd0;
char_seen <= 0;
debug <= 0;
cls_running <= 0;
cls_flag <= 1;
h_cnt <= 10'd0;
v_cnt <= 10'd0;
v_dot <= 5'd0;
end
else
begin
if (cls_flag || clr_screen_btn)
if (h_cnt < h_pixels)
h_cnt <= h_cnt + 1;
else
begin
if ((vpos == 0) && (hpos == 0))
cls_running <= 1;
// reset horizontal counters
h_cnt <= 0;
if (cls_running)
if (v_cnt < v_lines)
begin
// clear the vram using the position pointers
// very similar to the original apple 1 :)
vram_w_addr <= ((vpos * 40) + {4'b0, hpos});
vram_din <= 6'd0;
vram_w_en <= 1;
v_cnt <= v_cnt + 1;
if ((vpos == 23) && (hpos == 40))
// count 20 rows, so 480px / 20 = 24 rows
if (v_active)
begin
cls_running <= 0;
v_dot <= v_dot + 1;
if (v_dot == 5'd19)
v_dot <= 0;
end
end
else
begin
cls_flag <= 0;
// reset vertical counters
v_cnt <= 0;
v_dot <= 0;
end
end
begin
vram_w_en <= 0;
end
end
if (address == 1'b0) // address low == TX register
begin
if (enable & w_en & ~char_seen)
begin
// incoming character
debug <= {8'd0, din};
char_seen <= 1;
// count 16 pixels, so 640px / 16 = 40 characters
assign h_dot = h_active ? h_cnt[3:0] : 4'd0;
case(din)
8'h8D:
begin
// handle carriage return
h_cursor <= 0;
v_cursor <= v_cursor + 1;
end
//////////////////////////////////////////////////////////////////////////
// Character ROM
8'h7F:
// ignore the DDR call to the PIA
h_cursor <= h_cursor + 1;
font_rom my_font_rom(
.clk(clk25),
.character(font_char),
.pixel(font_pixel),
.line(font_line),
.out(font_out)
);
default:
begin
vram_w_addr <= ((v_cursor * 40) + {4'b0, h_cursor});
vram_din <= {~din[6], din[4:0]};
vram_w_en <= 1;
//////////////////////////////////////////////////////////////////////////
// Video RAM
h_cursor <= h_cursor + 1;
end
endcase
vram my_vram(
.clk(clk25),
.read_addr(vram_r_addr),
.write_addr(vram_w_addr),
.r_en(h_active),
.w_en(vram_w_en),
.din(vram_din),
.dout(vram_dout)
);
if (h_cursor == 39)
begin
h_cursor <= 0;
v_cursor <= v_cursor + 1;
end
//////////////////////////////////////////////////////////////////////////
// Video Signal Generation
if (v_cursor == 23)
begin
// here we need to add the scroll, probably by moving the
// HEAD of vram up one line
v_cursor <= 0;
end
always @(posedge clk25 or posedge rst) begin
if (rst) begin
vram_h_addr <= 0;
vram_v_addr <= 0;
end else begin
// start the pipeline for reading vram and font details
// 3 pixel clock cycles early
if (h_dot == 4'hC)
vram_h_addr <= vram_h_addr + 1;
end
else if(~enable & ~w_en)
char_seen <= 0;
end
end
// advance to next row when last display line is reached for row
if (v_dot == 5'd19 && h_cnt == 10'd0)
vram_v_addr <= vram_v_addr + 10'h28;
if (blink_clken)
// clear the address registers if we're not in visible area
if (~h_active)
vram_h_addr <= 0;
if (~v_active)
vram_v_addr <= 0;
end
end
//////////////////////////////////////////////////////////////////////////
// Cursor blink
reg blink;
reg [22:0] blink_div;
always @(posedge clk25 or posedge rst)
begin
if (rst)
blink_div <= 0;
else
begin
blink_div <= blink_div + 1;
if (blink_div == 23'd0)
blink <= ~blink;
end
end
//////////////////////////////////////////////////////////////////////////
// Pipeline and VGA signals
// vram to font rom to display pipeline assignments
assign cursor = ({4'd0, h_cursor} + v_cursor);
assign vram_r_addr = (vram_start_addr + {4'd0, vram_h_addr} + vram_v_addr);
assign font_char = (vram_r_addr != cursor) ? vram_dout : (blink) ? 6'd0 : 6'd32;
assign font_pixel = h_dot + 1; // offset by one to get pixel into right cycle,
// font output one pixel clk behind
assign font_line = v_dot;
// vga signals out to monitor
assign vga_red = font_out;
assign vga_grn = font_out;
assign vga_blu = font_out;
assign vga_h_sync = (h_cnt < h_pulse) ? 0 : 1;
assign vga_v_sync = (v_cnt < v_pulse) ? 0 : 1;
//////////////////////////////////////////////////////////////////////////
// CPU control and hardware cursor
always @(posedge clk25 or posedge rst)
begin
if (rst)
begin
h_cursor <= 6'd0;
v_cursor <= 10'd0;
char_seen <= 0;
vram_start_addr <= 0;
end
else
begin
vram_w_en <= 0;
if (address == 1'b0) // address low == TX register
begin
if (enable & w_en & ~char_seen)
begin
// incoming character
char_seen <= 1;
case(din)
8'h8D:
begin
// handle carriage return
h_cursor <= 0;
v_cursor <= v_cursor + 10'h28;
end
8'h7F:
// ignore the DDR call to the PIA
h_cursor <= 0;
default:
begin
vram_w_addr <= cursor;
vram_din <= {~din[6], din[4:0]};
vram_w_en <= 1;
h_cursor <= h_cursor + 1;
end
endcase
if (h_cursor == 39)
begin
h_cursor <= 0;
v_cursor <= v_cursor + 1;
end
if (v_cursor == 920)
begin
// force cursor to start at bottom of screen
// FIXME: now we need to scroll
v_cursor <= 920;
vram_start_addr <= vram_start_addr + 10'h28;
end
if (vram_start_addr >= 920)
vram_start_addr <= 0;
/*
if (v_cursor == 23)
begin
// here we need to add the scroll, probably by moving the
// HEAD of vram up one line
vram_start_addr <= vram_start_addr + 10'h28;
end
*/
end
else if(~enable & ~w_en)
char_seen <= 0;
end
end
end
endmodule

View File

@ -33,7 +33,7 @@ module vram(
);
`ifdef SIM
parameter RAM_FILENAME = "../roms/ram.hex";
parameter RAM_FILENAME = "../roms/vga_vram.bin";
`else
parameter RAM_FILENAME = "../../roms/vga_vram.bin";
`endif
@ -41,7 +41,7 @@ module vram(
reg [5:0] ram_data[0:1023];
initial
$readmemb(RAM_FILENAME, ram_data, 0, 1024);
$readmemb(RAM_FILENAME, ram_data, 0, 1023);
always @(posedge clk)
begin
@ -50,4 +50,3 @@ module vram(
end
endmodule