URL
https://opencores.org/ocsvn/rtftextcontroller/rtftextcontroller/trunk
Subversion Repositories rtftextcontroller
[/] [rtftextcontroller/] [trunk/] [rtl/] [verilog/] [rfTextController.sv] - Rev 32
Compare with Previous | Blame | View Log
// ============================================================================
// __
// \\__/ o\ (C) 2006-2023 Robert Finch, Waterloo
// \ __ / All rights reserved.
// \/_// robfinch<remove>@finitron.ca
// ||
//
// rfTextController.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 256kB memory region to be reserved.
//
// Memory Map:
// 00000-3FFFF display ram
// 40000-7FFFF character bitmap ram
// 80000-800FF controller registers
//
//--------------------------------------------------------------------
// 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)
// 08h
// 5- 0 nnnnnn char height in pixels, maximum scan line
// 11- 8 wwww pixel size - width
// 15-12 hhhh pixel size - height
// 21-16 nnnnnn char width in pixels
// 24 r reset state bit
// 32 e controller enable
// 40 m multi-color mode
// 41 a anti-alias mode
// 48-52 nnnnn yscroll
// 56-60 nnnnn xscroll
// 10h
// 30- 0 cccccccc cccccccc color code for transparent background RGB 4,9,9,9 (only RGB 7,7,7 used)
// 63-32 cccc...cccc border color ZRGB 4,9,9,9
// 18h
// 30- 0 cccccccc cccccccc tile color code 1
// 62-32 cccccccc cccccccc tile color code 2
// 20h
// 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-13 ttt cursor image type (none, box, underline, sidebar, checker, solid
// 47-32 aaaaaaaa aaaaaaaa cursor position
// 28h
// 15- 0 aaaaaaaa aaaaaaaa start address (index into display memory)
// 30h
// 15- 0 aaaaaaaa aaaaaaaa font address in char bitmap memory
// 31-24 dddddd font ascent
// 63-32 nnnnnnnn nnnnnnnn font ram lock "LOCK" or "UNLK"
//--------------------------------------------------------------------
//
// 1209 LUTs / 1003 FFs / 48 BRAMs / 1 DSP
// ============================================================================
//`define USE_CLOCK_GATE
//`define SUPPORT_AAM 1
`define TC_RAM_ADDR 32'hEC000001
`define TC_CBM_ADDR 32'hEC040001
`define TC_REG_ADDR 32'hEC080001
module rfTextController(
rst_i, clk_i, cs_config_i, cs_io_i,
cti_i, cyc_i, stb_i, ack_o, wr_i, sel_i, adr_i, dat_i, dat_o,
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'd32;
parameter BUSWID = 32;
parameter TEXT_CELL_COUNT = 8192;
parameter CFG_BUS = 8'd0;
parameter CFG_DEVICE = 5'd1;
parameter CFG_FUNC = 3'd0;
parameter CFG_VENDOR_ID = 16'h0;
parameter CFG_DEVICE_ID = 16'h0;
parameter CFG_SUBSYSTEM_VENDOR_ID = 16'h0;
parameter CFG_SUBSYSTEM_ID = 16'h0;
parameter CFG_ROM_ADDR = 32'hFFFFFFF0;
// Syscon
input rst_i; // reset
input clk_i; // clock
input cs_config_i;
input cs_io_i;
// Slave signals
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 [BUSWID/8-1:0] sel_i; // byte lane select
input [31:0] adr_i; // address
input [BUSWID-1:0] dat_i; // data input
output reg [BUSWID-1:0] dat_o; // data output
// 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 [39:0] zrgb_i; // input pixel stream
output reg [39:0] zrgb_o; // output pixel stream
input xonoff_i;
integer n2,n3;
reg controller_enable;
reg [39:0] bkColor40, bkColor40d, bkColor40d2, bkColor40d3; // background color
reg [39:0] fgColor40, fgColor40d, fgColor40d2, fgColor40d3; // foreground color
wire [1:0] 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 [1:0] tileWidth; // width of tile in bytes (0=1,1=2,2=4,3=8)
reg [ 5:0] cursorStart, cursorEnd;
reg [15:0] cursorPos;
reg [2:0] cursorType;
reg [15:0] startAddress;
reg [15:0] fontAddress;
reg font_locked;
reg [5:0] fontAscent;
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 mcm; // multi-color mode
reg aam; // anti-alias 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 [20:0] txtBkColor; // background color code
wire [20:0] txtFgColor; // foreground color code
wire [5:0] txtZorder;
reg [30:0] txtTcCode; // transparent color code
reg [30:0] tileColor1;
reg [30:0] tileColor2;
reg bgt, bgtd, bgtd2;
wire [63:0] tdat_o;
wire [63:0] chdat_o;
reg [63:0] cfg_dat [0:31];
reg [63:0] cfg_out;
function [63:0] fnRbo;
input n;
input [63:0] i;
fnRbo = n ? {i[7:0],i[15:8],i[23:16],i[31:24],i[39:32],i[47:40],i[55:48],i[63:56]} : i;
endfunction
//--------------------------------------------------------------------
// bus interfacing
// Address Decoding
// I/O range Dx
//--------------------------------------------------------------------
// Register the inputs
reg cs_config;
reg cs_rom, cs_reg, cs_text, cs_any;
reg cs_rom1, cs_reg1, cs_text1;
reg cs_tc;
reg [17:0] radr_i;
reg [63:0] rdat_i;
reg rwr_i;
reg [7:0] rsel_i;
reg [7:0] wrs_i;
reg [31:0] tc_ram_addr;
reg [31:0] tc_cbm_addr;
reg [31:0] tc_reg_addr;
always_ff @(posedge clk_i)
cs_any <= cyc_i & stb_i & cs_io_i;
always_ff @(posedge clk_i)
cs_config <= cyc_i & stb_i & cs_config_i && adr_i[27:20]==CFG_BUS && adr_i[19:15]==CFG_DEVICE && adr_i[14:12]==CFG_FUNC;
always_ff @(posedge clk_i)
cs_rom1 <= adr_i[31:18] == tc_cbm_addr[31:18];
always_ff @(posedge clk_i)
cs_reg1 <= adr_i[31: 8] == tc_reg_addr[31: 8];
always_ff @(posedge clk_i)
cs_text1 <= adr_i[31:18] == tc_ram_addr[31:18];
always_comb
cs_rom <= cs_rom1 && cs_any;
always_comb
cs_reg <= cs_reg1 && cs_any;
always_comb
cs_text <= cs_text1 && cs_any;
always_ff @(posedge clk_i)
wrs_i <= (BUSWID==64) ? {8{wr_i}} & sel_i :
adr_i[2] ? {{4{wr_i}} & sel_i,4'h0} : {4'h0,{4{wr_i}} & sel_i};
always_ff @(posedge clk_i)
rwr_i <= wr_i;
always_ff @(posedge clk_i)
rsel_i <= (BUSWID==64) ? sel_i : adr_i[2] ? {sel_i,4'h0} : {4'h0,sel_i};
always_ff @(posedge clk_i)
radr_i <= adr_i;
always_ff @(posedge clk_i)
rdat_i <= (BUSWID==64) ? dat_i : (BUSWID==32) ? {2{dat_i}} : {4{dat_i}};
// Register outputs
always_ff @(posedge clk_i)
if (BUSWID==64)
casez({cs_config,cs_rom,cs_reg,cs_text})
4'b1???: dat_o <= cfg_out;
4'b01??: dat_o <= chdat_o;
4'b001?: dat_o <= rego;
4'b0001: dat_o <= tdat_o;
default: dat_o <= 'h0;
endcase
else if (BUSWID==32)
casez({cs_config,cs_rom,cs_reg,cs_text})
4'b1???: dat_o <= radr_i[2] ? cfg_out[63:32] : cfg_out[31:0];
4'b01??: dat_o <= radr_i[2] ? chdat_o[63:32] : chdat_o[31:0];
4'b001?: dat_o <= radr_i[2] ? rego[63:32] : rego[31:0];
4'b0001: dat_o <= radr_i[2] ? tdat_o[63:32] : tdat_o[31:0];
default: dat_o <= 'd0;
endcase
else
dat_o <= 'd0;
//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 (
.rst_i(rst_i),
.clk_i(clk_i),
.ce_i(1'b1),
.i((cs_any|cs_config) & ~rwr_i),
.we_i((cs_any|cs_config) & rwr_i),
.o(ack_o),
.rid_i(0),
.wid_i(0),
.rid_o(),
.wid_o()
);
//--------------------------------------------------------------------
// config
//--------------------------------------------------------------------
initial begin
for (n3 = 0; n3 < 32; n3 = n3 + 1)
cfg_dat[n3] = 'd0;
end
always_ff @(posedge clk_i)
if (rst_i) begin
tc_ram_addr <= `TC_RAM_ADDR;
tc_cbm_addr <= `TC_CBM_ADDR;
tc_reg_addr <= `TC_REG_ADDR;
end
else begin
if (cs_config) begin
if (rwr_i)
case(radr_i[7:3])
5'h02:
begin
if (&rsel_i[3:0] && rdat_i[31:0]==32'hFFFFFFFF)
tc_ram_addr <= 32'hFFFFFFFF; // no memory is needed
else begin
if (rsel_i[0]) tc_ram_addr[7:0] <= rdat_i[7:0];
if (rsel_i[1]) tc_ram_addr[15:8] <= rdat_i[15:8];
if (rsel_i[2]) tc_ram_addr[23:16] <= rdat_i[23:16];
if (rsel_i[3]) tc_ram_addr[31:24] <= rdat_i[31:24];
end
if (&rsel_i[7:4] && rdat_i[31:0]==32'hFFFFFFFF)
tc_cbm_addr <= 32'hFFFFFFFF; // no memory is needed
else begin
if (rsel_i[4]) tc_cbm_addr[7:0] <= rdat_i[7:0];
if (rsel_i[5]) tc_cbm_addr[15:8] <= rdat_i[15:8];
if (rsel_i[6]) tc_cbm_addr[23:16] <= rdat_i[23:16];
if (rsel_i[7]) tc_cbm_addr[31:24] <= rdat_i[31:24];
end
end
5'h03:
begin
if (&rsel_i[3:0] && rdat_i[31:0]==32'hFFFFFFFF)
tc_reg_addr <= 32'hFFFFFFFF; // no memory is needed
else begin
if (rsel_i[0]) tc_reg_addr[7:0] <= rdat_i[7:0];
if (rsel_i[1]) tc_reg_addr[15:8] <= rdat_i[15:8];
if (rsel_i[2]) tc_reg_addr[23:16] <= rdat_i[23:16];
if (rsel_i[3]) tc_reg_addr[31:24] <= rdat_i[31:24];
end
end
default:
cfg_dat[radr_i[7:3]] <= rdat_i;
endcase
else
case(radr_i[7:3])
5'h00: cfg_out <= {32'h0,CFG_DEVICE_ID,CFG_VENDOR_ID};
5'h01: cfg_out <= {8'h00,8'h00,8'h00,8'd32,24'h0,8'h0};
5'h02: cfg_out <= {tc_cbm_addr,tc_ram_addr};
5'h03: cfg_out <= {32'hFFFFFFFF,tc_reg_addr};
5'h04: cfg_out <= 64'hFFFFFFFFFFFFFFFF;
5'h05: cfg_out <= {CFG_SUBSYSTEM_ID,CFG_SUBSYSTEM_VENDOR_ID,32'h0};
5'h06: cfg_out <= {24'h00,8'h00,CFG_ROM_ADDR};
5'h07: cfg_out <= {8'd8,8'd0,8'd0,8'd0,32'h0};
default: cfg_out <= cfg_dat[radr_i[7:3]];
endcase
end
end
//--------------------------------------------------------------------
//--------------------------------------------------------------------
`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_ff @(posedge vclk)
txtAddr <= startAddress + rowcol + col;
// Register read-back memory
// This core to be found under Memory-Cores folder
// 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 [26:0] lfsr1_o;
lfsr27 #(.WID(27)) ulfsr1(rst_i, dot_clk_i, 1'b1, 1'b0, lfsr1_o);
wire [63:0] lfsr_o = {6'h10,
lfsr1_o[26:24],4'b0,lfsr1_o[23:21],4'b0,lfsr1_o[20:18],4'b0,
lfsr1_o[17:15],4'b0,lfsr1_o[14:12],4'b0,lfsr1_o[11:9],4'b0,
7'h00,lfsr1_o[8:0]
};
wire [63:0] lfsr_o2 = {6'h10,
// lfsr1_o[26:24],4'b0,lfsr1_o[23:21],4'b0,lfsr1_o[20:18],4'b0,
// lfsr1_o[17:15],4'b0,lfsr1_o[14:12],4'b0,lfsr1_o[11:9],4'b0,
4'b0,lfsr1_o[26:24],4'b0,lfsr1_o[23:21],lfsr1_o[20:18],4'b0,
4'b0,lfsr1_o[17:15],4'b0,lfsr1_o[14:12],lfsr1_o[11:9],4'b0,
7'h00,lfsr1_o[8:0]
};
wire [63:0] lfsr_o1 = {lfsr1_o[3:0],2'b00,
// lfsr1_o[26:24],4'b0,lfsr1_o[23:21],4'b0,lfsr1_o[20:18],4'b0,
// lfsr1_o[17:15],4'b0,lfsr1_o[14:12],4'b0,lfsr1_o[11:9],4'b0,
4'b0,lfsr1_o[26:24],4'b0,lfsr1_o[23:21],lfsr1_o[20:18],4'b0,
4'b0,lfsr1_o[17:15],lfsr1_o[14:12],4'b0,4'b0,lfsr1_o[11:9],
7'h00,lfsr1_o[8:0]
};
/* This snippit of code for performing burst accesses, under construction.
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
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
rfTextScreenRam #(
.TEXT_CELL_COUNT(TEXT_CELL_COUNT)
)
screen_ram1
(
.clka_i(clk_i),
.csa_i(cs_text),
.wea_i(rwr_i),
.sela_i(rsel_i),
.adra_i(radr_i[16:3]),
.data_i(rdat_i),
.data_o(tdat_o),
.clkb_i(vclk),
.csb_i(ld_shft|por),
.web_i(por),
.selb_i(8'hFF),
.adrb_i(txtAddr[13:0]),
.datb_i(lfsr_o),//txtAddr[12:0] > 13'd1664 ? lfsr_o1 : lfsr_o),
.datb_o(screen_ram_out)
);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Character bitmap RAM
// - room for 8160 8x8 characters
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
wire [63:0] char_bmp; // character ROM output
rfTextCharRam charRam0
(
.clk_i(clk_i),
.cs_i(cs_rom),
.we_i(rwr_i & ~font_locked),
.sel_i(rsel_i),
.adr_i(radr_i[15:3]),
.dat_i(rdat_i[63:0]),
.dat_o(chdat_o),
.dot_clk_i(vclk),
.ce_i(ld_shft),
.fontAddress_i(fontAddress),
.char_code_i(screen_ram_out[12:0]),
.maxScanpix_i(maxScanpix),
.maxscanline_i(maxScanlinePlusOne),
.scanline_i(rowscan[5:0]),
.bmp_o(char_bmp)
);
// pipeline delay - sync color with character bitmap output
reg [20:0] txtBkCode1;
reg [20:0] txtFgCode1;
reg [5:0] txtZorder1;
always @(posedge vclk)
if (ld_shft) txtBkCode1 <= screen_ram_out[36:16];
always @(posedge vclk)
if (ld_shft) txtFgCode1 <= screen_ram_out[57:37];
always @(posedge vclk)
if (ld_shft) txtZorder1 <= screen_ram_out[63:58];
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register read port
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
always_comb
if (cs_reg)
rego <= rrm_o;
else
rego <= 64'h0000;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register write port
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
always_ff @(posedge clk_i)
if (rst_i) begin
por <= 1'b1;
mcm <= 1'b0;
aam <= 1'b0;
controller_enable <= 1'b1;
xscroll <= 5'd0;
yscroll <= 5'd0;
txtTcCode <= 24'h1ff;
bdrColor <= 32'hFFBF2020;
startAddress <= 16'h0000;
fontAddress <= 16'h0008;
font_locked <= 1'b1;
fontAscent <= 6'd12;
cursorStart <= 5'd00;
cursorEnd <= 5'd31;
cursorPos <= 16'h0003;
cursorType <= 3'd4; // checker
// 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
*/
// 64x32
if (num==4'd1) begin
windowTop <= 12'd4058;//12'd16;
windowLeft <= 12'd3918;//12'd3956;//12'd86;
pixelWidth <= 4'd0; // 800 pixels
pixelHeight <= 4'd0; // 600 pixels
numCols <= COLS;
numRows <= ROWS;
maxRowScan <= 6'd17;
maxScanpix <= 6'd11;
rBlink <= 3'b111; // 01 = non display
charOutDelay <= 8'd5;
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 <= 5'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[4: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[20:16];
if (rsel_i[3]) por <= rdat_i[24];
if (rsel_i[4]) controller_enable <= rdat_i[32];
if (rsel_i[5])
begin
mcm <= rdat_i[40];
aam <= rdat_i[41];
end
if (rsel_i[6]) yscroll <= rdat_i[52:48];
if (rsel_i[7]) xscroll <= rdat_i[60: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[23:16] <= rdat_i[23:16];
if (rsel_i[3]) txtTcCode[30:24] <= rdat_i[30:24];
if (rsel_i[4]) bdrColor[7:0] <= rdat_i[39:32];
if (rsel_i[5]) bdrColor[15:8] <= rdat_i[47:40];
if (rsel_i[6]) bdrColor[23:16] <= rdat_i[55:48];
if (rsel_i[7]) bdrColor[31:24] <= rdat_i[63:56];
end
4'd3: // Color Control 2
begin
if (rsel_i[0]) tileColor1[7:0] <= rdat_i[7:0];
if (rsel_i[1]) tileColor1[15:8] <= rdat_i[15:8];
if (rsel_i[2]) tileColor1[23:16] <= rdat_i[23:16];
if (rsel_i[3]) tileColor1[30:24] <= rdat_i[30:24];
if (rsel_i[4]) tileColor2[7:0] <= rdat_i[39:32];
if (rsel_i[5]) tileColor2[15:8] <= rdat_i[47:40];
if (rsel_i[6]) tileColor2[23:16] <= rdat_i[55:48];
if (rsel_i[7]) tileColor2[30:24] <= rdat_i[62:56];
end
4'd4: // 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:13];
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'd5: // 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'd6: //
begin
if (rsel_i[0]) fontAddress[7:0] <= rdat_i[7:0];
if (rsel_i[1]) fontAddress[15:8] <= rdat_i[15:8];
if (rsel_i[3]) fontAscent[5:0] <= rdat_i[5:0];
if (&rsel_i[7:4]) begin
if (rdat_i[63:32]=="LOCK")
font_locked <= 1'b1;
else if (rdat_i[63:32]=="UNLK")
font_locked <= 1'b0;
end
end
default: ;
endcase
end
end
//--------------------------------------------------------------------
// Cursor image is computed based on the font size, so the available
// hardware cursors are really simple. More sophisticated hardware
// cursors can be had via the sprite controller.
//--------------------------------------------------------------------
reg [31:0] curout;
wire [31:0] curout1;
always_ff @(posedge vclk)
if (ld_shft) begin
curout = 'd0;
case(cursorType)
// No cursor
3'd0: ;
// "Box" cursor
3'd1:
begin
case(rowscan)
maxRowScan,5'd0: curout = 32'hFFFFFFFF;
/*
maxRowScan-1:
if (rowscan==maxRowScan-1) begin
curout[maxScanpix[5:1]] = 1'b1;
curout[maxScanpix[5:1]+1] = 1'b1;
end
*/
default:
begin
curout[maxScanpix] = 1'b1;
curout[0] = 1'b1;
end
endcase
end
// Vertical Line cursor
3'd2: curout[maxScanpix] = 1'b1;
// Underline cursor
3'd3:
if (rowscan==fontAscent)
curout = 32'hFFFFFFFF;
// Checker cursor
3'd4: curout = rowscan[1] ? 32'h33333333 : 32'hCCCCCCCC;
// Solid cursor
3'd7: curout = 32'hFFFFFFFF;
default: curout = 32'hFFFFFFFF;
endcase
end
ft_delay
#(
.WID(32),
.DEP(3)
)
uftd1
(
.clk(vclk),
.ce(ld_shft),
.i(curout),
.o(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()
);
// 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 <= 5'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
rowscan <= yscroll;
else if (rowscan==maxRowScan)
rowscan <= 5'd0;
else
rowscan <= rowscan + 5'd1;
end
end
assign nxt_col = colscan==maxScanpix;
always @(posedge vclk)
if (sym_rst)
colscan <= 5'd0;
else begin
if (nhp) begin
if (hctr==12'd0)
colscan <= xscroll;
else if (nxt_col)
colscan <= 5'd0;
else
colscan <= colscan + 5'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_ff @(posedge vclk)
maxScanlinePlusOne <= maxRowScan + 4'd1;
// Blink counter
//
always_ff @(posedge vclk)
if (sym_rst)
bcnt <= 6'd0;
else begin
if (pe_vsync)
bcnt <= bcnt + 6'd1;
end
reg blink_en;
always_ff @(posedge vclk)
blink_en <= (cursorPos+charOutDelay-2'd1==txtAddr);// && (rowscan[4:0] >= cursorStart) && (rowscan[4:0] <= 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_ff @(posedge vclk)
if (ld_shft)
bkColor40 <= {txtZorder1[5:2],txtBkCode1[20:14],5'b0,txtBkCode1[13:7],5'b0,txtBkCode1[6:0],5'b0};
always_ff @(posedge vclk)
if (ld_shft)
bkColor40d <= bkColor40;
always_ff @(posedge vclk)
if (ld_shft)
bkColor40d2 <= bkColor40d;
always_ff @(posedge vclk)
if (nhp)
bkColor40d3 <= bkColor40d2;
always_ff @(posedge vclk)
if (ld_shft)
fgColor40 <= {txtZorder1[5:2],txtFgCode1[20:14],5'b0,txtFgCode1[13:7],5'b0,txtFgCode1[6:0],5'b0};
always_ff @(posedge vclk)
if (ld_shft)
fgColor40d <= fgColor40;
always_ff @(posedge vclk)
if (ld_shft)
fgColor40d2 <= fgColor40d;
always_ff @(posedge vclk)
if (nhp)
fgColor40d3 <= fgColor40d2;
always_ff @(posedge vclk)
if (ld_shft)
bgt <= txtBkCode1=={txtTcCode[26:20],txtTcCode[17:11],txtTcCode[8:2]};
always_ff @(posedge vclk)
if (ld_shft)
bgtd <= bgt;
always_ff @(posedge vclk)
if (nhp)
bgtd2 <= bgtd;
// Convert character bitmap to pixels
reg [63:0] charout1;
always_ff @(posedge vclk)
charout1 <= blink ? (char_bmp ^ curout1) : char_bmp;
// Convert parallel to serial
rfTextShiftRegister ups1
(
.rst(rst_i),
.clk(vclk),
.mcm(mcm),
// .aam(aam),
.ce(nhp),
.ld(ld_shft),
.a(maxScanpix[5:0]),
.qin(2'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.
wire bpix = hctr[2] ^ rowscan[4];// ^ blink;
always_ff @(posedge vclk)
if (nhp)
iblank <= (row >= numRows) || (col >= numCols + charOutDelay) || (col < charOutDelay);
`ifdef SUPPORT_AAM
function [11:0] fnBlendComponent;
input [11:0] c1;
input [11:0] c2;
input [1:0] pix;
case(pix)
2'b00: fnBlendComponent = c2;
2'b01: fnBlendComponent = ((c1 * 4'd5) + (c2 * 4'd11)) >> 4;
2'b10: fnBlendComponent = ((c1 * 4'd11) + (c2 * 4'd5)) >> 4;
2'b11: fnBlendComponent = c1;
endcase
endfunction
function [39:0] fnBlend;
input [39:0] c1;
input [39:0] c2;
input [1:0] pix;
fnBlend = {
|pix ? c1[39:36] : c2[39:36],
fnBlendComponent(c1[35:24],c2[35:24]),
fnBlendComponent(c1[23:12],c2[23:12]),
fnBlendComponent(c1[11: 0],c2[11: 0])
};
endfunction
`endif
// 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_ff @(posedge dot_clk_i)
casez({controller_enable&xonoff_i,blank_i,iblank,border_i,bpix,mcm,aam,pix})
9'b01???????: zrgb_o <= 40'h00000000;
9'b11???????: zrgb_o <= 40'h00000000;
9'b1001?????: zrgb_o <= {bdrColor[30:27],bdrColor[26:18],3'b0,bdrColor[17:9],3'b0,bdrColor[8:0],3'b0};
`ifdef SUPPORT_AAM
9'b1000?01??: zrgb_o <= fnBlend(fgColor40d3,zrgb_i[39:36] > bkColor40d3[39:36]) ? zrgb_i : bkColor40d3, pix);
`endif
9'b1000?000?: zrgb_o <= (zrgb_i[39:36] > bkColor40d3[39:36]) ? zrgb_i : bkColor40d3;
9'b1000?001?: zrgb_o <= fgColor40d3; // ToDo: compare z-order
9'b1000?1000: zrgb_o <= (zrgb_i[39:36] > bkColor40d3[39:36]) ? zrgb_i : bkColor40d3;
9'b1000?1001: zrgb_o <= fgColor40d3;
9'b1000?1010: zrgb_o <= {tileColor1[30:27],tileColor1[26:18],3'b0,tileColor1[17:9],3'b0,tileColor1[8:0],3'b0};
9'b1000?1011: zrgb_o <= {tileColor2[30:27],tileColor2[26:18],3'b0,tileColor2[17:9],3'b0,tileColor2[8:0],3'b0};
// 6'b1010?0: zrgb_o <= bgtd ? zrgb_i : bkColor32d;
default: zrgb_o <= zrgb_i;
endcase
endmodule