module MulDivDCM(input xtal, output clk); parameter div = 5; parameter mul = 2; wire CLKFX_BUF; wire GND_BIT = 0; BUFG CLKFX_BUFG_INST (.I(CLKFX_BUF), .O(clk)); DCM_SP DCM_SP_INST (.CLKFB(GND_BIT), .CLKIN(xtal), .DSSEN(GND_BIT), .PSCLK(GND_BIT), .PSEN(GND_BIT), .PSINCDEC(GND_BIT), .RST(GND_BIT), .CLKFX(CLKFX_BUF)); defparam DCM_SP_INST.CLK_FEEDBACK = "NONE"; defparam DCM_SP_INST.CLKDV_DIVIDE = 2.0; defparam DCM_SP_INST.CLKFX_DIVIDE = div; defparam DCM_SP_INST.CLKFX_MULTIPLY = mul; defparam DCM_SP_INST.CLKIN_DIVIDE_BY_2 = "FALSE"; defparam DCM_SP_INST.CLKIN_PERIOD = 20.000; defparam DCM_SP_INST.CLKOUT_PHASE_SHIFT = "NONE"; defparam DCM_SP_INST.DESKEW_ADJUST = "SYSTEM_SYNCHRONOUS"; defparam DCM_SP_INST.DFS_FREQUENCY_MODE = "LOW"; defparam DCM_SP_INST.DLL_FREQUENCY_MODE = "LOW"; defparam DCM_SP_INST.DUTY_CYCLE_CORRECTION = "TRUE"; defparam DCM_SP_INST.FACTORY_JF = 16'hC080; defparam DCM_SP_INST.PHASE_SHIFT = 0; defparam DCM_SP_INST.STARTUP_WAIT = "TRUE"; endmodule module VTerm( input xtal, output wire vs, hs, output reg [2:0] red, output reg [2:0] green, output reg [1:0] blue, input serrx ); wire clk25; wire [11:0] x, y; wire border; MulDivDCM dcm25(xtal, clk25); defparam dcm25.div = 4; defparam dcm25.mul = 2; SyncGen sync(clk25, vs, hs, x, y, border); wire [7:0] cschar; wire [2:0] csrow; wire [7:0] csdata; wire [10:0] vraddr; wire [7:0] vrdata; wire [10:0] vwaddr; wire [7:0] vwdata; wire [7:0] serdata; wire vwr, serwr; wire [10:0] vscroll; wire odata; CharSet cs(cschar, csrow, csdata); VideoRAM vram(clk25, vraddr + vscroll, vrdata, vwaddr, vwdata, vwr); VDisplay dpy(clk25, x, y, vraddr, vrdata, cschar, csrow, csdata, odata); SerRX rx(clk25, serwr, serdata, serrx); RXState rxsm(clk25, vwr, vwaddr, vwdata, vscroll, serwr, serdata); always @(posedge clk25) begin red <= border ? 0 : {3{odata}}; green <= border ? 0 : {3{odata}}; blue <= border ? 0 : {2{odata}}; end endmodule module SyncGen( input pixclk, output reg vs, hs, output reg [11:0] x, y, output reg border); parameter XRES = 640; parameter XFPORCH = 16; parameter XSYNC = 96; parameter XBPORCH = 48; parameter YRES = 480; parameter YFPORCH = 10; parameter YSYNC = 2; parameter YBPORCH = 29; always @(posedge pixclk) begin if (x >= (XRES + XFPORCH + XSYNC + XBPORCH)) begin if (y >= (YRES + YFPORCH + YSYNC + YBPORCH)) y = 0; else y = y + 1; x = 0; end else x = x + 1; hs <= (x >= (XRES + XFPORCH)) && (x < (XRES + XFPORCH + XSYNC)); vs <= (y >= (YRES + YFPORCH)) && (y < (YRES + YFPORCH + YSYNC)); border <= (x > XRES) || (y > YRES); end endmodule module CharSet( input [7:0] char, input [2:0] row, output wire [7:0] data); reg [7:0] rom [(256 * 8 - 1):0]; initial $readmemb("ibmpc1.mem", rom); assign data = rom[{char, row}]; endmodule module VideoRAM( input pixclk, input [10:0] raddr, output reg [7:0] rdata, input [10:0] waddr, input [7:0] wdata, input wr); reg [7:0] ram [2047 : 0]; always @(posedge pixclk) rdata <= ram[raddr]; always @(posedge pixclk) if (wr) ram[waddr] <= wdata; endmodule module VDisplay( input pixclk, input [11:0] x, input [11:0] y, output wire [10:0] raddr, input [7:0] rchar, output wire [7:0] cschar, output wire [2:0] csrow, input [7:0] csdata, output reg data ); wire [7:0] col = x[11:3]; wire [5:0] row = y[9:3]; reg [7:0] ch; reg [11:0] xdly; assign raddr = ({row,4'b0} + {row,6'b0} + {4'h0,col}); assign cschar = rchar; assign csrow = y[2:0]; always @(posedge pixclk) xdly <= x; always @(posedge pixclk) data = ((xdly < 80 * 8) && (y < 25 * 8)) ? csdata[7 - xdly[2:0]] : 0; endmodule `define IN_CLK 25000000 `define OUT_CLK 57600 `define CLK_DIV (`IN_CLK / `OUT_CLK) module SerRX( input pixclk, output reg wr = 0, output reg [7:0] wchar = 0, input serialrx ); reg [15:0] rx_clkdiv = 0; reg [3:0] rx_state = 4'b0000; reg [7:0] rx_data_tmp; always @(posedge pixclk) begin if ((rx_state == 0) && (serialrx == 0) /*&& (rx_hasdata == 0)*/) /* Kick off. */ rx_state <= 4'b0001; else if ((rx_state != 4'b0000) && (rx_clkdiv == 0)) begin if (rx_state != 4'b1010) rx_state <= rx_state + 1; else rx_state <= 0; case (rx_state) 4'b0001: begin end /* Twiddle thumbs -- this is the end of the half bit. */ 4'b0010: rx_data_tmp[0] <= serialrx; 4'b0011: rx_data_tmp[1] <= serialrx; 4'b0100: rx_data_tmp[2] <= serialrx; 4'b0101: rx_data_tmp[3] <= serialrx; 4'b0110: rx_data_tmp[4] <= serialrx; 4'b0111: rx_data_tmp[5] <= serialrx; 4'b1000: rx_data_tmp[6] <= serialrx; 4'b1001: rx_data_tmp[7] <= serialrx; 4'b1010: if (serialrx == 1) begin wr <= 1; wchar <= rx_data_tmp; end endcase end if (wr) wr <= 0; if ((rx_state == 0) && (serialrx == 0) /*&& (rx_hasdata == 0)*/) /* Wait half a period before advancing. */ rx_clkdiv <= `CLK_DIV / 2 + `CLK_DIV / 4; else if (rx_clkdiv == `CLK_DIV) rx_clkdiv <= 0; else rx_clkdiv <= rx_clkdiv + 1; end endmodule module RXState( input clk25, output reg vwr = 0, output reg [10:0] vwaddr = 0, output reg [7:0] vwdata = 0, output reg [10:0] vscroll = 0, input serwr, input [7:0] serdata); parameter STATE_IDLE = 4'b0000; parameter STATE_NEWLINE = 4'b0001; parameter STATE_CLEAR = 4'b0010; reg [3:0] state = STATE_CLEAR; reg [6:0] x = 0; reg [4:0] y = 0; reg [10:0] clearstart = 0; reg [10:0] clearend = 11'b11111111111; always @(posedge clk25) case (state) STATE_IDLE: if (serwr) begin if (serdata == 8'h0A) begin state <= STATE_NEWLINE; vwr <= 0; end else if (serdata == 8'h0D) begin x <= 0; vwr <= 0; end else if (serdata == 8'h0C) begin clearstart <= 0; clearend <= 11'b11111111111; x <= 0; y <= 0; vscroll <= 0; state <= STATE_CLEAR; end else begin vwr <= 1; vwaddr <= ({y,4'b0} + {y,6'b0} + {4'h0,x}) + vscroll; vwdata <= serdata; if (x == 79) begin x <= 0; state <= STATE_NEWLINE; end else x <= x + 1; end end STATE_NEWLINE: begin vwr <= 0; if (y == 24) begin vscroll <= vscroll + 80; clearstart <= (25 * 80) + vscroll; clearend <= (26*80) + vscroll; state <= STATE_CLEAR; end else begin y <= y + 1; state <= STATE_IDLE; end end STATE_CLEAR: begin vwr <= 1; vwaddr <= clearstart; vwdata <= 8'h20; clearstart <= clearstart + 1; if (clearstart == clearend) state <= STATE_IDLE; end endcase endmodule