URL
https://opencores.org/ocsvn/rtftextcontroller/rtftextcontroller/trunk
Subversion Repositories rtftextcontroller
[/] [rtftextcontroller/] [trunk/] [rtl/] [verilog/] [GFX_TextController.sv] - Rev 30
Compare with Previous | Blame | View Log
// ============================================================================
// __
// \\__/ o\ (C) 2006-2020 Robert Finch, Waterloo
// \ __ / All rights reserved.
// \/_// robfinch<remove>@finitron.ca
// ||
//
// GFX_TextController.sv
// text controller
//
// BSD 3-Clause License
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
// Text Controller
//
// FEATURES
//
// This core requires an external timing generator to provide horizontal
// and vertical sync signals, but otherwise can be used as a display
// controller on it's own. However, this core may also be embedded within
// another core such as a VGA controller.
//
// Window positions are referenced to the rising edge of the vertical and
// horizontal sync pulses.
//
// The core includes an embedded dual port RAM to hold the screen
// characters.
//
// The controller expects a 128kB memory region to be reserved.
//
// Memory Map:
// 00000-0FFFF display ram
// 17F00-17FFF controller registers
// 18000-1FFFF character bitmap ram
//
//--------------------------------------------------------------------
// Registers
//
// 00h
// 7 - 0 cccccccc number of columns (horizontal displayed number of characters)
// 15- 8 rrrrrrrr number of rows (vertical displayed number of characters)
// 19-16 dddd character output delay
// 43-32 nnnn nnnnnnnn window left (horizontal sync position - reference for left edge of displayed)
// 59-48 nnnn nnnnnnnn window top (vertical sync position - reference for the top edge of displayed)
// 01h
// 4- 0 nnnnn maximum scan line (char ROM max value is 7)
// 11- 8 wwww pixel size - width
// 15-12 hhhh pixel size - height
// 24 r reset state bit
// 48-52 nnnnn yscroll
// 56-60 nnnnn xscroll
// 02h
// 20- 0 cccccccc cccccccc color code for transparent background RGB 7,7,7
// 63-32 cccc...cccc border color ZRGB 8,8,8,8
// 03h
// 4- 0 eeeee cursor end
// 7- 5 bbb blink control
// BP: 00=no blink
// BP: 01=no display
// BP: 10=1/16 field rate blink
// BP: 11=1/32 field rate blink
// 12- 8 sssss cursor start
// 15-14 tt cursor image type (box, underline, sidebar, asterisk
// 47-32 aaaaaaaa aaaaaaaa cursor position
// 04h
// 15- 0 aaaaaaaa aaaaaaaa start address (index into display memory)
// 05h
// 15-0 aaaaaaaa aaaaaaaa font address
// 07h
// 150 - - aaaaaaaa aaaaaaaa light pen position
//--------------------------------------------------------------------
//
// ============================================================================
//`define USE_CLOCK_GATE
module GFX_TextController(
rst_i, clk_i, cs_i,
cti_i, cyc_i, stb_i, ack_o, wr_i, sel_i, adr_i, dat_i, dat_o,
lp_i,
dot_clk_i, hsync_i, vsync_i, blank_i, border_i, zrgb_i, zrgb_o, xonoff_i
);
parameter num = 4'd1;
parameter COLS = 8'd64;
parameter ROWS = 8'd33;
// Syscon
input rst_i; // reset
input clk_i; // clock
// Slave signals
input cs_i; // circuit select
input [2:0] cti_i;
input cyc_i; // valid bus cycle
input stb_i; // data strobe
output ack_o; // data acknowledge
input wr_i; // write
input [ 7:0] sel_i; // byte lane select
input [16:0] adr_i; // address
input [63:0] dat_i; // data input
output reg [63:0] dat_o; // data output
input lp_i; // light pen
// Video signals
input dot_clk_i; // video dot clock
input hsync_i; // end of scan line
input vsync_i; // end of frame
input blank_i; // blanking signal
input border_i; // border area
input [31:0] zrgb_i; // input pixel stream
output reg [31:0] zrgb_o; // output pixel stream
input xonoff_i;
integer n;
reg controller_enable;
reg [31:0] bkColor32, bkColor32d; // background color
reg [31:0] fgColor32, fgColor32d; // foreground color
wire pix; // pixel value from character generator 1=on,0=off
reg por;
wire vclk;
reg [63:0] rego;
reg [5:0] yscroll;
reg [5:0] xscroll;
reg [11:0] windowTop;
reg [11:0] windowLeft;
reg [ 7:0] numCols;
reg [ 7:0] numRows;
reg [ 7:0] charOutDelay;
reg [ 1:0] mode;
reg [ 5:0] maxRowScan;
reg [ 5:0] maxScanpix;
reg [ 5:0] cursorStart, cursorEnd;
reg [15:0] cursorPos;
reg [1:0] cursorType;
reg [15:0] fontAddress;
reg [15:0] startAddress;
reg [ 2:0] rBlink;
reg [31:0] bdrColor; // Border color
reg [ 3:0] pixelWidth; // horizontal pixel width in clock cycles
reg [ 3:0] pixelHeight; // vertical pixel height in scan lines
reg ecm; // extended color mode
wire [11:0] hctr; // horizontal reference counter (counts clocks since hSync)
wire [11:0] scanline; // scan line
reg [ 7:0] row; // vertical reference counter (counts rows since vSync)
reg [ 7:0] col; // horizontal column
reg [ 5:0] rowscan; // scan line within row
reg [ 5:0] colscan; // pixel column number within cell
wire nxt_row; // when to increment the row counter
wire nxt_col; // when to increment the column counter
reg [ 5:0] bcnt; // blink timing counter
wire blink;
reg iblank;
reg [5:0] maxScanlinePlusOne;
wire nhp; // next horizontal pixel
wire ld_shft = nxt_col & nhp;
// display and timing signals
reg [15:0] txtAddr; // index into memory
reg [15:0] penAddr;
wire [63:0] screen_ram_out; // character code
wire [63:0] char_bmp; // character ROM output
wire [20:0] txtBkColor; // background color code
wire [20:0] txtFgColor; // foreground color code
wire [5:0] txtZorder;
reg [20:0] txtTcCode; // transparent color code
reg bgt, bgtd;
wire [63:0] tdat_o;
wire [2:0] scanindex = rowscan[2:0];
//--------------------------------------------------------------------
// bus interfacing
// Address Decoding
// I/O range Dx
//--------------------------------------------------------------------
// Register the inputs
reg cs_rom, cs_reg, cs_text, cs_any;
reg [16:0] radr_i;
reg [63:0] rdat_i;
reg rwr_i;
reg [7:0] rsel_i;
reg [7:0] wrs_i;
always @(posedge clk_i)
cs_rom <= cs_i && cyc_i && stb_i && (adr_i[16:8] > 9'h17F);
always @(posedge clk_i)
cs_reg <= cs_i && cyc_i && stb_i && (adr_i[16:8] == 9'h17F);
always @(posedge clk_i)
cs_text <= cs_i && cyc_i && stb_i && (adr_i[16:8] < 9'h100);
always @(posedge clk_i)
cs_any <= cs_i && cyc_i && stb_i;
always @(posedge clk_i)
wrs_i <= {8{wr_i}} & sel_i;
always @(posedge clk_i)
rwr_i <= wr_i;
always @(posedge clk_i)
rsel_i <= sel_i;
always @(posedge clk_i)
radr_i[16:3] <= adr_i[16:3];
// Recreate LSB's for charram
always @(posedge clk_i)
begin
radr_i[0] <= sel_i[1]|sel_i[3]|sel_i[5]|sel_i[7];
radr_i[1] <= |sel_i[3:2] | |sel_i[7:6];
radr_i[2] <= |sel_i[7:4];
end
always @(posedge clk_i)
rdat_i <= dat_i;
// Register outputs
// The output is "sticky" to give more hold time.
always @(posedge clk_i)
casez({cs_rom,cs_reg,cs_text})
3'b01?: dat_o <= rego;
3'b001: dat_o <= tdat_o;
default: dat_o <= dat_o;
endcase
//always @(posedge clk_i)
// if (cs_text) begin
// $display("TC WRite: %h %h", adr_i, dat_i);
// $stop;
// end
// - there is a four cycle latency for reads, an ack is generated
// after the synchronous RAM read
// - writes can be acknowledged right away.
ack_gen #(
.READ_STAGES(5),
.WRITE_STAGES(1),
.REGISTER_OUTPUT(1)
)
uag1 (
.clk_i(clk_i),
.ce_i(1'b1),
.i(cs_any),
.we_i(cs_any & rwr_i),
.o(ack_o)
);
//--------------------------------------------------------------------
//--------------------------------------------------------------------
`ifdef USE_CLOCK_GATE
BUFHCE ucb1 (.I(dot_clk_i), .CE(controller_enable), .O(vclk));
`else
assign vclk = dot_clk_i;
`endif
//--------------------------------------------------------------------
// Video Memory
//--------------------------------------------------------------------
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Address Calculation:
// - Simple: the row times the number of cols plus the col plus the
// base screen address
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
reg [15:0] rowcol;
always @(posedge vclk)
if (ld_shft)
txtAddr <= startAddress + rowcol + col;
// Register read-back memory
// Allows reading back of register values by shadowing them with ram
wire [3:0] rrm_adr = radr_i[6:3];
wire [63:0] rrm_o;
regReadbackMem #(.WID(8)) rrm0L
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[0]),
.i(rdat_i[7:0]),
.o(rrm_o[7:0])
);
regReadbackMem #(.WID(8)) rrm0H
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[1]),
.i(rdat_i[15:8]),
.o(rrm_o[15:8])
);
regReadbackMem #(.WID(8)) rrm1L
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[2]),
.i(rdat_i[23:16]),
.o(rrm_o[23:16])
);
regReadbackMem #(.WID(8)) rrm1H
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[3]),
.i(rdat_i[31:24]),
.o(rrm_o[31:24])
);
regReadbackMem #(.WID(8)) rrm2L
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[4]),
.i(rdat_i[39:32]),
.o(rrm_o[39:32])
);
regReadbackMem #(.WID(8)) rrm2H
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[5]),
.i(rdat_i[47:40]),
.o(rrm_o[47:40])
);
regReadbackMem #(.WID(8)) rrm3L
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[6]),
.i(rdat_i[55:48]),
.o(rrm_o[55:48])
);
regReadbackMem #(.WID(8)) rrm3H
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i & rsel_i[7]),
.i(rdat_i[63:56]),
.o(rrm_o[63:56])
);
wire [23:0] lfsr1_o;
lfsr #(24) ulfsr1(rst_i, dot_clk_i, 1'b1, 1'b0, lfsr1_o);
wire [63:0] lfsr_o = {6'h20,
lfsr1_o[23:21],4'b0,lfsr1_o[20:18],4'b0,lfsr1_o[17:16],5'b0,
lfsr1_o[15:13],4'b0,lfsr1_o[12:10],4'b0,lfsr1_o[9:8],5'b0,
8'h00,lfsr1_o[7:0]
};
/* The following code was for WB burst access to the text memory. About the
only time burst access is used is for screen clear. This is just code bloat.
wire pe_cs;
edge_det u1(.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(cs_text), .pe(pe_cs), .ne(), .ee() );
reg [14:0] ctr;
always @(posedge clk_i)
if (pe_cs) begin
if (cti_i==3'b000)
ctr <= adr_i[16:3];
else
ctr <= adr_i[16:3] + 12'd1;
cnt <= 3'b000;
end
else if (cs_text && cnt[2:0]!=3'b100 && cti_i!=3'b000) begin
ctr <= ctr + 2'd1;
cnt <= cnt + 3'd1;
end
reg [13:0] radr;
always @(posedge clk_i)
radr <= pe_cs ? adr_i[16:3] : ctr;
*/
// text screen RAM
wire [15:0] bram_adr = radr_i[15:0];
syncRam8kx64 screen_ram1
(
.clka(clk_i),
.ena(cs_text),
.wea(wrs_i),
.addra(bram_adr[15:3]),
.dina(rdat_i),
.douta(tdat_o),
.clkb(vclk),
.enb(ld_shft|por),
.web({8{por}}),
.addrb(txtAddr[12:0]),
.dinb(lfsr_o),
.doutb(screen_ram_out)
);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Character bitmap ROM/RAM
// - 32kB
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
char_ram charRam0
(
.clk_i(clk_i),
.cs_i(cs_rom),
.we_i(1'b0),
.adr_i(bram_adr[14:0]),
.dat_i(rdat_i >> {bram_adr[2:0],3'b0}),
.dot_clk_i(vclk),
.ce_i(ld_shft),
.fontAddress_i(fontAddress),
.char_code_i(screen_ram_out[11:0]),
.maxScanpix_i(maxScanpix),
.maxscanline_i(maxScanlinePlusOne),
.scanline_i(rowscan),
.bmp_o(char_bmp)
);
// pipeline delay - sync color with character bitmap output
wire [20:0] txtBkCode1;
wire [20:0] txtFgCode1;
wire [5:0] txtZorder1;
delay #(.WID(21),.DEP(3)) udlyb (.clk(vclk), .ce(ld_shft), .i(screen_ram_out[36:16]), .o(txtBkCode1));
delay #(.WID(21),.DEP(3)) udlyf (.clk(vclk), .ce(ld_shft), .i(screen_ram_out[57:37]), .o(txtFgCode1));
delay #(.WID( 6),.DEP(3)) udlyz (.clk(vclk), .ce(ld_shft), .i(screen_ram_out[63:58]), .o(txtZorder1));
//--------------------------------------------------------------------
// Light Pen
//--------------------------------------------------------------------
wire lpe;
edge_det u1 (.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(lp_i), .pe(lpe), .ne(), .ee() );
always @(posedge clk_i)
if (rst_i)
penAddr <= 32'h0000_0000;
else begin
if (lpe)
penAddr <= txtAddr;
end
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register read port
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
always @*
if (cs_reg) begin
case(rrm_adr)
4'd7: rego <= penAddr;
default: rego <= rrm_o;
endcase
end
else
rego <= 64'h0000;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register write port
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
always @(posedge clk_i)
if (rst_i) begin
por <= 1'b1;
controller_enable <= 1'b1;
xscroll <= 6'd0;
yscroll <= 6'd0;
txtTcCode <= 16'h1ff;
bdrColor <= 32'hFFBF2020;
startAddress <= 16'h0000;
fontAddress <= 16'h0000;
cursorStart <= 5'd00;
cursorEnd <= 5'd31;
cursorPos <= 16'h0003;
cursorType <= 2'b00;
// 104x63
/*
windowTop <= 12'd26;
windowLeft <= 12'd260;
pixelWidth <= 4'd0;
pixelHeight <= 4'd1; // 525 pixels (408 with border)
*/
// 52x31
/*
// 84x47
windowTop <= 12'd16;
windowLeft <= 12'd90;
pixelWidth <= 4'd1; // 681 pixels
pixelHeight <= 4'd1; // 384 pixels
*/
// 48x29
if (num==4'd1) begin
windowTop <= 12'd4058;//12'd16;
windowLeft <= 12'd3964;//12'd3930;//12'd86;
pixelWidth <= 4'd0; // 1280 pixels
pixelHeight <= 4'd0; // 720 pixels
numCols <= COLS;
numRows <= ROWS;
maxRowScan <= 6'd17;
maxScanpix <= 6'd11;
rBlink <= 3'b111; // 01 = non display
charOutDelay <= 8'd7;
end
else if (num==4'd2) begin
windowTop <= 12'd4032;//12'd16;
windowLeft <= 12'd3720;//12'd86;
pixelWidth <= 4'd0; // 800 pixels
pixelHeight <= 4'd0; // 600 pixels
numCols <= 40;
numRows <= 25;
maxRowScan <= 6'd7;
maxScanpix <= 6'd7;
rBlink <= 3'b111; // 01 = non display
charOutDelay <= 8'd6;
end
end
else begin
if (bcnt > 6'd10)
por <= 1'b0;
if (cs_reg & rwr_i) begin // register write ?
$display("TC Write: r%d=%h", rrm_adr, rdat_i);
case(rrm_adr)
4'd0: begin
if (rsel_i[0]) numCols <= rdat_i[7:0];
if (rsel_i[1]) numRows <= rdat_i[15:8];
if (rsel_i[2]) charOutDelay <= rdat_i[23:16];
if (rsel_i[4]) windowLeft[7:0] <= rdat_i[39:32];
if (rsel_i[5]) windowLeft[11:8] <= rdat_i[43:40];
if (rsel_i[6]) windowTop[7:0] <= rdat_i[55:48];
if (rsel_i[7]) windowTop[11:8] <= rdat_i[59:56];
end
4'd1:
begin
if (rsel_i[0]) maxRowScan <= rdat_i[5:0];
if (rsel_i[1]) begin
pixelHeight <= rdat_i[15:12];
pixelWidth <= rdat_i[11:8]; // horizontal pixel width
end
if (rsel_i[2]) maxScanpix <= rdat_i[21:16];
if (rsel_i[3]) por <= rdat_i[24];
if (rsel_i[4]) controller_enable <= rdat_i[32];
if (rsel_i[6]) yscroll <= rdat_i[53:48];
if (rsel_i[7]) xscroll <= rdat_i[61:56];
end
4'd2: // Color Control
begin
if (rsel_i[0]) txtTcCode[7:0] <= rdat_i[7:0];
if (rsel_i[1]) txtTcCode[15:8] <= rdat_i[15:8];
if (rsel_i[2]) txtTcCode[20:16] <= rdat_i[20:16];
if (rsel_i[4]) bdrColor[7:0] <= dat_i[39:32];
if (rsel_i[5]) bdrColor[15:8] <= dat_i[47:40];
if (rsel_i[6]) bdrColor[23:16] <= dat_i[55:48];
if (rsel_i[7]) bdrColor[31:24] <= dat_i[63:56];
end
4'd3: // Cursor Control
begin
if (rsel_i[0]) begin
cursorEnd <= rdat_i[4:0]; // scan line sursor starts on
rBlink <= rdat_i[7:5];
end
if (rsel_i[1]) begin
cursorStart <= rdat_i[12:8]; // scan line cursor ends on
cursorType <= rdat_i[15:14];
end
if (rsel_i[4]) cursorPos[7:0] <= rdat_i[39:32];
if (rsel_i[5]) cursorPos[15:8] <= rdat_i[47:40];
end
4'd4: // Page flipping / scrolling
begin
if (rsel_i[0]) startAddress[7:0] <= rdat_i[7:0];
if (rsel_i[1]) startAddress[15:8] <= rdat_i[15:8];
end
4'd5:
begin
if (rsel_i[0]) fontAddress[7:0] <= rdat_i[7:0];
if (rsel_i[1]) fontAddress[15:8] <= rdat_i[15:8];
end
default: ;
endcase
end
end
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// "Box" cursor bitmap
(* ram_style="block" *)
reg [31:0] curram [0:511];
reg [31:0] curout, curout1;
initial begin
// Box cursor
curram[0] = 8'b11111110;
curram[1] = 8'b10000010;
curram[2] = 8'b10000010;
curram[3] = 8'b10000010;
curram[4] = 8'b10000010;
curram[5] = 8'b10000010;
curram[6] = 8'b10010010;
curram[7] = 8'b11111110;
// vertical bar cursor
curram[32] = 8'b11000000;
curram[33] = 8'b10000000;
curram[34] = 8'b10000000;
curram[35] = 8'b10000000;
curram[36] = 8'b10000000;
curram[37] = 8'b10000000;
curram[38] = 8'b10000000;
curram[39] = 8'b11000000;
// underline cursor
curram[64] = 8'b00000000;
curram[65] = 8'b00000000;
curram[66] = 8'b00000000;
curram[67] = 8'b00000000;
curram[68] = 8'b00000000;
curram[69] = 8'b00000000;
curram[70] = 8'b00000000;
curram[71] = 8'b11111111;
// Asterisk
curram[96] = 8'b00000000;
curram[97] = 8'b00000000;
curram[98] = 8'b00100100;
curram[99] = 8'b00011000;
curram[100] = 8'b01111110;
curram[101] = 8'b00011000;
curram[102] = 8'b00100100;
curram[103] = 8'b00000000;
end
always @(posedge vclk)
if (ld_shft)
curout1 <= curram[{cursorType,rowscan}];
always @(posedge vclk)
curout <= curout1;
//-------------------------------------------------------------
// Video Stuff
//-------------------------------------------------------------
wire pe_hsync;
wire pe_vsync;
edge_det edh1
(
.rst(rst_i),
.clk(vclk),
.ce(1'b1),
.i(hsync_i),
.pe(pe_hsync),
.ne(),
.ee()
);
edge_det edv1
(
.rst(rst_i),
.clk(vclk),
.ce(1'b1),
.i(vsync_i),
.pe(pe_vsync),
.ne(),
.ee()
);
// Horizontal counter:
//
/*
HVCounter uhv1
(
.rst(rst_i),
.vclk(vclk),
.pixcce(1'b1),
.sync(hsync_i),
.cnt_offs(windowLeft),
.pixsz(pixelWidth),
.maxpix(maxScanpix),
.nxt_pix(nhp),
.pos(col),
.nxt_pos(nxt_col),
.ctr(hctr)
);
*/
// Vertical counter:
//
/*
HVCounter uhv2
(
.rst(rst_i),
.vclk(vclk),
.pixcce(pe_hsync),
.sync(vsync_i),
.cnt_offs(windowTop),
.pixsz(pixelHeight),
.maxpix(maxRowScan),
.nxt_pix(),
.pos(row),
.nxt_pos(nxt_row),
.ctr(scanline)
);
*/
// We generally don't care about the exact reset point, unless debugging in
// simulation. The counters will eventually cycle to a proper state. A little
// bit of logic / routing can be avoided by omitting the reset.
`ifdef SIM
wire sym_rst = rst_i;
`else
wire sym_rst = 1'b0;
`endif
// Raw scanline counter
vid_counter #(12) u_vctr (.rst(sym_rst), .clk(vclk), .ce(pe_hsync), .ld(pe_vsync), .d(windowTop), .q(scanline), .tc());
vid_counter #(12) u_hctr (.rst(sym_rst), .clk(vclk), .ce(1'b1), .ld(pe_hsync), .d(windowLeft), .q(hctr), .tc());
// Vertical pixel height counter, synchronized to scanline #0
reg [3:0] vpx;
wire nvp = vpx==pixelHeight;
always @(posedge vclk)
if (sym_rst)
vpx <= 4'b0;
else begin
if (pe_hsync) begin
if (scanline==12'd0)
vpx <= 4'b0;
else if (nvp)
vpx <= 4'd0;
else
vpx <= vpx + 4'd1;
end
end
reg [3:0] hpx;
assign nhp = hpx==pixelWidth;
always @(posedge vclk)
if (sym_rst)
hpx <= 4'b0;
else begin
if (hctr==12'd0)
hpx <= 4'b0;
else if (nhp)
hpx <= 4'd0;
else
hpx <= hpx + 4'd1;
end
// The scanline row within a character bitmap
always @(posedge vclk)
if (sym_rst)
rowscan <= 6'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
rowscan <= yscroll;
else if (rowscan==maxRowScan)
rowscan <= 6'd0;
else
rowscan <= rowscan + 1'd1;
end
end
assign nxt_col = colscan==maxScanpix;
always @(posedge vclk)
if (sym_rst)
colscan <= 6'd0;
else begin
if (nhp) begin
if (hctr==12'd0)
colscan <= xscroll;
else if (nxt_col)
colscan <= 6'd0;
else
colscan <= colscan + 1'd1;
end
end
// The screen row
always @(posedge vclk)
if (sym_rst)
row <= 8'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
row <= 8'd0;
else if (rowscan==maxRowScan)
row <= row + 8'd1;
end
end
// The screen column
always @(posedge vclk)
if (sym_rst)
col <= 8'd0;
else begin
if (hctr==12'd0)
col <= 8'd0;
else if (nhp) begin
if (nxt_col)
col <= col + 8'd1;
end
end
// More useful, the offset of the start of the text display on a line.
always @(posedge vclk)
if (sym_rst)
rowcol <= 16'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
rowcol <= 8'd0;
else if (rowscan==maxRowScan)
rowcol <= rowcol + numCols;
end
end
// Takes 3 clock for scanline to become stable, but should be stable before any
// chars are displayed.
reg [13:0] rxmslp1;
always @(posedge vclk)
maxScanlinePlusOne <= maxRowScan + 1'd1;
//always @(posedge vclk)
// rxmslp1 <= row * maxScanlinePlusOne;
//always @(posedge vclk)
// scanline <= scanline - rxmslp1;
// Blink counter
//
always @(posedge vclk)
if (sym_rst)
bcnt <= 6'd0;
else begin
if (pe_vsync)
bcnt <= bcnt + 6'd1;
end
reg blink_en;
always @(posedge vclk)
blink_en <= (cursorPos+3==txtAddr) && (rowscan >= cursorStart) && (rowscan <= cursorEnd);
VT151 ub2
(
.e_n(!blink_en),
.s(rBlink),
.i0(1'b1), .i1(1'b0), .i2(bcnt[4]), .i3(bcnt[5]),
.i4(1'b1), .i5(1'b0), .i6(bcnt[4]), .i7(bcnt[5]),
.z(blink),
.z_n()
);
always @(posedge vclk)
if (ld_shft)
bkColor32 <= {txtZorder1,2'b00,txtBkCode1[20:14],1'b0,txtBkCode1[13:7],1'b0,txtBkCode1[6:0],1'b0};
always @(posedge vclk)
if (nhp)
bkColor32d <= bkColor32;
always @(posedge vclk)
if (ld_shft)
fgColor32 <= {txtZorder1,2'b00,txtFgCode1[20:14],1'b0,txtFgCode1[13:7],1'b0,txtFgCode1[6:0],1'b0};
always @(posedge vclk)
if (nhp)
fgColor32d <= fgColor32;
always @(posedge vclk)
if (ld_shft)
bgt <= txtBkCode1==txtTcCode;
always @(posedge vclk)
if (nhp)
bgtd <= bgt;
reg [63:0] charout1;
always @(posedge vclk)
charout1 <= blink ? (char_bmp ^ curout) : char_bmp;
// Convert parallel to serial
ParallelToSerial ups1
(
.rst(rst_i),
.clk(vclk),
.ce(nhp),
.ld(ld_shft),
.a(maxScanpix[5:3]),
.qin(1'b0),
.d(charout1),
.qh(pix)
);
// Pipelining Effect:
// - character output is delayed by 2 or 3 character times relative to the video counters
// depending on the resolution selected
// - this means we must adapt the blanking signal by shifting the blanking window
// two or three character times.
always @(posedge vclk)
if (nhp)
iblank <= (row >= numRows) || (col >= numCols + charOutDelay) || (col < charOutDelay);
wire bpix = hctr[2] ^ rowscan[4];// ^ blink;
// Choose between input RGB and controller generated RGB
// Select between foreground and background colours.
// Note the ungated dot clock must be used here, or output from other
// controllers would not be visible if the clock were gated off.
always @(posedge dot_clk_i)
casez({controller_enable&xonoff_i,blank_i,iblank,border_i,bpix,pix})
6'b?1????: zrgb_o <= 32'h00000000;
6'b1001??: zrgb_o <= bdrColor;
//6'b10010?: zrgb_o <= 32'hFFBF2020;
//6'b10011?: zrgb_o <= 32'hFFDFDFDF;
6'b1000?0: zrgb_o <= ((zrgb_i[31:24] <= bkColor32d[31:24]) || bgtd) ? zrgb_i : bkColor32d;
6'b1000?1: zrgb_o <= fgColor32d; // ToDo: compare z-order
// 6'b1010?0: zrgb_o <= bgtd ? zrgb_i : bkColor32d;
// 6'b1010?1: zrgb_o <= fgColor32d;
default: zrgb_o <= zrgb_i;
endcase
endmodule