1
0
mirror of synced 2026-01-11 23:53:00 +00:00
This commit is contained in:
brad 2007-01-02 16:24:48 +00:00
commit 823db1be62
18 changed files with 3658 additions and 0 deletions

1167
v/char_rom.v Normal file

File diff suppressed because it is too large Load Diff

465
v/crt.v Normal file
View File

@ -0,0 +1,465 @@
// crt.v
//
// Extremely basic crt ram management
//
// insert character
// check for printable
// check for cr,lf
// scroll one line
// advance start
// clear last line
// move to begining of line
// lf
// move to next line
// cr
// move to begining of line
//
module crt(reset_n, clock,
insert, done, data, clearing,
ram_addr, ram_data, ram_we_n, ram_wclk, ram_wslot,
cursorh, cursorv);
input reset_n;
input clock;
input insert;
output reg done;
output reg clearing;
input [7:0] data;
output [11:0] ram_addr;
output reg [7:0] ram_data;
output reg ram_we_n;
output [6:0] cursorh;
output [5:0] cursorv;
output reg ram_wclk;
input ram_wslot;
reg [6:0] cursor_h;
reg [5:0] cursor_v;
// conditions
wire eol, scroll;
// output of state machine
reg inc_h, clr_h;
reg inc_v, clr_v;
reg set_newline;
reg set_done;
// external state
reg newline;
reg [11:0] offset;
reg inc_offset, clr_offset;
reg [3:0] state, nextstate;
wire printable;
reg [2:0] write_delay;
// offset + v*80 + h
// factored into (v*64 + v*16) + h
assign ram_addr =
//debug /*offset +*/
{cursor_v, 6'b0} + {2'b0, cursor_v, 4'b0} +
{4'b000, cursor_h};
assign printable = ((data[6:0] > 7'h20) && (data[6:0] < 7'h7f)) ?
1'b1 : 1'b0;
// one-hot states
parameter [3:0]
T_RESET = 4'd0,
T_CLEARALL = 4'd1,
T_CLEARALL_NEXT = 4'd2,
T_IDLE = 4'd3,
T_PREWRITE = 4'd4,
T_WRITE = 4'd5,
T_POSTWRITE = 4'd6,
T_NEWLINE = 4'd7,
T_SCROLL = 4'd8,
T_CLEARLAST = 4'd9,
T_CLEARLAST_WRITE = 4'd10,
T_CLEARLAST_NEXT = 4'd11,
T_CLEARLAST_DONE = 4'd12;
// don't change unless you fix the factored *80 in ram_addr
parameter [6:0] COLS = 80;
parameter [5:0] LINES = 25;
// manage incrementing offset
always @(posedge clock or negedge reset_n)
if (~reset_n)
offset <= 12'b0;
else
begin
if (inc_offset)
if (offset == (LINES-1)*COLS)
offset <= 12'b0;
else
offset <= offset + COLS;
end
// manage clearing offset when it rolls over
always @(posedge clock or negedge reset_n)
if (~reset_n)
clr_offset <= 0;
else
begin
if (offset >= LINES*COLS)
clr_offset <= 1;
else
clr_offset <= 0;
end
// manage incrementing h
always @(posedge clock or negedge reset_n)
if (~reset_n)
cursor_h <= 7'b0;
else
begin
if (inc_h)
cursor_h <= cursor_h + 1;
else
if (clr_h)
cursor_h <= 7'b0;
end
// manage incrementing v
always @(posedge clock or negedge reset_n)
if (~reset_n)
cursor_v <= 6'b0;
else
begin
if (inc_v)
cursor_v <= cursor_v + 1;
else
if (clr_v)
cursor_v <= 6'b0;
end
// manage end of line
assign eol = cursor_h == (COLS-1);
// and end of screen
assign scroll = cursor_v == (LINES-1);
// manage external state
always @(posedge clock or negedge reset_n)
if (~reset_n)
begin
ram_data <= 8'b0;
done <= 1'b0;
newline <= 1'b0;
write_delay <= 3'b0;
end
else
begin
//debug
if (state == T_CLEARALL_NEXT)
begin
if (cursor_h == 7'd1)
ram_data <= 8'h30 + {5'b0, cursor_v[5:3]};
else
if (cursor_h == 7'd2)
ram_data <= 8'h30 + {5'b0, cursor_v[2:0]};
else
ram_data <= 8'h30 + {1'b0, cursor_h};
// ram_data <= 8'h40;
// ram_data <= 8'h20 + {1'b0, cursor_h} + { 2'b00, cursor_v};
end
if (state == T_CLEARLAST_WRITE)
ram_data <= 8'b0;
if (state == T_IDLE)
begin
newline <= 1'b0;
done <= 1'b0;
if (insert)
ram_data <= data;
end
if (state == T_WRITE ||
state == T_CLEARLAST_WRITE ||
state == T_CLEARALL)
write_delay <= write_delay + 1;
else
write_delay <= 3'd0;
if (set_newline)
newline <= 1'b1;
if (set_done)
done <= 1'b1;
end
// next state
always @(posedge clock or negedge reset_n)
if (~reset_n)
state <= T_RESET;
else
state <= nextstate;
// insert state machine
always @(state or insert or eol or newline or scroll or
printable or data or write_delay or ram_wslot)
begin
inc_h = 1'b0;
clr_h = 1'b0;
inc_v = 1'b0;
clr_v = 1'b0;
inc_offset = 1'b0;
set_newline = 1'b0;
set_done = 1'b0;
clearing = 1'b0;
ram_we_n = 1'b1;
case (state) // synthesis full_case
T_RESET:
begin
clearing = 1'b1;
nextstate = T_CLEARALL;
nextstate = T_IDLE;
end
T_CLEARALL:
begin
clearing = 1'b1;
ram_we_n = 1'b0;
ram_wclk = (write_delay == 3'd1) ? 1 : 0;
nextstate = (write_delay == 3'd1) ? T_CLEARALL_NEXT : T_CLEARALL;
end
T_CLEARALL_NEXT:
begin
clearing = 1'b1;
nextstate = T_CLEARALL;
if (eol)
begin
//debug
// if (cursor_v == 15)
if (scroll)
begin
clr_v = 1'b1;
clr_h = 1'b1;
nextstate = T_IDLE;
end
else
begin
clr_h = 1'b1;
inc_v = 1'b1;
end
end
else
inc_h = 1'b1;
end
T_IDLE:
begin
clearing = 1'b0;
if (insert)
begin
// if printable character, write to ram
if (printable)
nextstate = T_PREWRITE;
else
nextstate = T_POSTWRITE;
end
else
nextstate = T_IDLE;
end
T_PREWRITE:
begin
// wait until we're in the write slot
nextstate = ram_wslot ? T_WRITE : T_PREWRITE;
if (eol)
begin
set_newline = 1'b1;
clr_h = 1'b1;
end
end
T_WRITE:
begin
ram_we_n = 1'b0;
// delay until ram_wclk catches up
ram_wclk = (write_delay == 3'd1) ? 1 : 0;
nextstate = (write_delay == 3'd1) ? T_POSTWRITE : T_WRITE;
end
T_POSTWRITE:
begin
set_done = 1'b1;
// cr
if (data[6:0] == 7'h0d)
clr_h = 1'b1;
else
inc_h = 1'b1;
// eol or lf
if (newline || (data[6:0] == 7'h0a))
nextstate = T_NEWLINE;
else
nextstate = T_IDLE;
end
T_NEWLINE:
begin
clr_h = 1'b1;
if (scroll)
nextstate = T_SCROLL;
else
begin
inc_v = 1'b1;
nextstate = T_IDLE;
end
end
T_SCROLL:
begin
clr_h = 1'b1;
inc_offset = 1'b1;
nextstate = T_CLEARLAST;
end
T_CLEARLAST:
begin
nextstate = T_CLEARLAST_WRITE;
end
T_CLEARLAST_WRITE:
begin
ram_we_n = 1'b0;
// delay until ram_wclk catches up
ram_wclk = (write_delay == 3'd1) ? 1 : 0;
nextstate = (write_delay == 3'd1) ?
T_CLEARLAST_NEXT : T_CLEARLAST_WRITE;
end
T_CLEARLAST_NEXT:
begin
inc_h = 1'b1;
ram_we_n = 1'b1;
nextstate = T_CLEARLAST_WRITE;
if (eol)
nextstate = T_CLEARLAST_DONE;
end
T_CLEARLAST_DONE:
begin
clr_h = 1'b1;
nextstate = T_IDLE;
end
endcase
end
assign cursorh = cursor_h;
assign cursorv = cursor_v;
endmodule
// ------------------
//`define TEST
`ifdef TEST
`timescale 1ns / 1ns
module test;
reg clk, reset_n;
reg insert;
wire done;
reg [7:0] data;
integer count;
reg free;
wire [11:0] ram_addr;
wire [7:0] ram_data;
wire ram_we_n;
wire [6:0] cursorh;
wire [5:0] cursorv;
crt _crt(reset_n, clk,
insert, done, data,
ram_addr, ram_data, ram_we_n,
cursorh, cursorv);
// defparam _crt.LINES = 4;
// defparam _crt.COLS = 4;
initial
begin
$timeformat(-9, 0, "ns", 7);
$dumpfile("crt.vcd");
$dumpvars(0, test._crt);
end
always @(posedge done)
if (free)
begin
data = 8'h41 + count;
count = count + 1;
if (count == 3)
begin
count = 0;
data = 8'o212;
end
end
initial
begin
clk = 0;
reset_n = 1;
insert = 0;
data = 0;
count = 0;
free = 0;
#1 reset_n = 0;
#100 reset_n = 1;
#200 begin insert = 1; data = 8'h41; end
#80 insert = 0;
#200 begin insert = 1; data = 8'h42; end
#80 insert = 0;
#200 begin insert = 1; data = 8'h0d; end
#80 insert = 0;
#200 begin insert = 1; data = 8'h0a; end
#80 insert = 0;
#200 begin insert = 1; data = 8'h43; end
#80 insert = 0;
// #50000 $finish;
#10000 $finish;
end
always
begin
#20 clk = 0;
#20 clk = 1;
end
endmodule
`endif // `ifdef TEST

27
v/fpga.ucf Normal file
View File

@ -0,0 +1,27 @@
net clka loc=r8;
#net clkb loc=b8;
net reset_n loc=e3;
net ps2_clk loc=f4;
net ps2_data loc=e1;
net vga_blue0 loc=h4;
net vga_blue1 loc=k3;
net vga_blue2 loc=l5;
net vga_green0 loc=h2;
net vga_green1 loc=k5;
net vga_green2 loc=r1;
net vga_red0 loc=j1;
net vga_red1 loc=m1;
net vga_red2 loc=t2;
net vga_hsync_n loc=k4;
net vga_vsync_n loc=k1;
net fpga_din_d0 loc=d14;
net fpga_d1 loc=e16;
net fpga_d2 loc=f15;
net fpga_d3 loc=g16;
net fpga_d4 loc=j16;
net fpga_d5 loc=m16;
net fpga_d6 loc=n16;
net fpga_d7 loc=n14;

89
v/fpga.v Normal file
View File

@ -0,0 +1,89 @@
// fpga.v
module fpga (clka,
reset_n,
ps2_clk,
ps2_data,
vga_blue0,
vga_blue1,
vga_blue2,
vga_green0,
vga_green1,
vga_green2,
vga_red0,
vga_red1,
vga_red2,
vga_hsync_n,
vga_vsync_n,
fpga_din_d0,
fpga_d1,
fpga_d2,
fpga_d3,
fpga_d4,
fpga_d5,
fpga_d6,
fpga_d7
);
input clka; // 100mhz
input reset_n;
input ps2_clk, ps2_data;
output vga_blue0, vga_blue1, vga_blue2;
output vga_green0, vga_green1, vga_green2;
output vga_red0, vga_red1, vga_red2;
output vga_hsync_n, vga_vsync_n;
output fpga_din_d0, fpga_d1, fpga_d2, fpga_d3,
fpga_d4, fpga_d5, fpga_d6, fpga_d7;
//
wire hsync, vsync;
wire [8:0] pixel;
//
wire [7:0] led_data;
// signals to create a 25MHz clock from the 100MHz input clock
wire clk25;
reg [1:0] gray_cnt;
// clock divider by 4 to for a slower clock
// uses grey code for minimized logic
always @(posedge clka or negedge reset_n)
if (~reset_n)
gray_cnt <= 2'b00;
else
case (gray_cnt)
2'b00: gray_cnt <= 2'b01;
2'b01: gray_cnt <= 2'b11;
2'b11: gray_cnt <= 2'b10;
2'b10: gray_cnt <= 2'b00;
default: gray_cnt <= 2'b00;
endcase
// assign 25mhz clock
assign clk25 = gray_cnt[1];
vga vga (.reset_n(reset_n),
.clock(clk25),
.pixel(pixel),
.blank_n(),
.hsync(hsync),
.vsync(vsync),
.ps2_clk(ps2_clk),
.ps2_data(ps2_data),
.led_data(led_data));
assign vga_hsync_n = ~hsync;
assign vga_vsync_n = ~vsync;
assign {vga_red2, vga_red1, vga_red0,
vga_green2, vga_green1, vga_green0,
vga_blue2, vga_blue1, vga_blue0} = pixel;
assign {fpga_din_d0, fpga_d1, fpga_d2, fpga_d3,
fpga_d4, fpga_d5, fpga_d6, fpga_d7} = led_data;
endmodule // fpga

27
v/fpga2.ucf Normal file
View File

@ -0,0 +1,27 @@
net clka loc=r8;
#net clkb loc=b8;
net reset_n loc=e3;
net ps2_clk loc=f4;
net ps2_data loc=e1;
#net vga_blue0 loc=h4;
#net vga_blue1 loc=k3;
#net vga_blue2 loc=l5;
#net vga_green0 loc=h2;
#net vga_green1 loc=k5;
#net vga_green2 loc=r1;
#net vga_red0 loc=j1;
#net vga_red1 loc=m1;
#net vga_red2 loc=t2;
#net vga_hsync_n loc=k4;
#net vga_vsync_n loc=k1;
net fpga_din_d0 loc=d14;
net fpga_d1 loc=e16;
net fpga_d2 loc=f15;
net fpga_d3 loc=g16;
net fpga_d4 loc=j16;
net fpga_d5 loc=m16;
net fpga_d6 loc=n16;
net fpga_d7 loc=n14;

100
v/fpga2.v Normal file
View File

@ -0,0 +1,100 @@
// fpga2.v
// simple test module for ps2 module
module fpga (clka,
clkb,
reset_n,
ps2_clk,
ps2_data,
vga_blue0,
vga_blue1,
vga_blue2,
vga_green0,
vga_green1,
vga_green2,
vga_red0,
vga_red1,
vga_red2,
vga_hsync_n,
vga_vsync_n,
fpga_din_d0,
fpga_d1,
fpga_d2,
fpga_d3,
fpga_d4,
fpga_d5,
fpga_d6,
fpga_d7
);
input clka; // 100mhz
input clkb; // 50mhz
input reset_n;
input ps2_clk, ps2_data;
output vga_blue0, vga_blue1, vga_blue2;
output vga_green0, vga_green1, vga_green2;
output vga_red0, vga_red1, vga_red2;
output vga_hsync_n, vga_vsync_n;
output fpga_din_d0, fpga_d1, fpga_d2, fpga_d3,
fpga_d4, fpga_d5, fpga_d6, fpga_d7;
//
wire hsync, vsync;
wire [8:0] pixel;
// signals to create a 25MHz clock from the 100MHz input clock
wire clk25;
reg [1:0] gray_cnt;
// clock divider by 4 to for a slower clock
// uses grey code for minimized logic
always @(posedge clka or negedge reset_n)
if (~reset_n)
gray_cnt <= 2'b00;
else
case (gray_cnt)
2'b00: gray_cnt <= 2'b01;
2'b01: gray_cnt <= 2'b11;
2'b11: gray_cnt <= 2'b10;
2'b10: gray_cnt <= 2'b00;
default: gray_cnt <= 2'b00;
endcase
// assign the clock that this entity runs off
assign clk25 = gray_cnt[1];
wire kb_rdy;
wire kb_bsy;
wire [7:0] kb_scancode;
wire [7:0] data;
ps2 ps2(.clk(clk25),
.rst_n(reset_n),
.ps2_clk(ps2_clk),
.ps2_data(ps2_data),
.scancode(kb_scancode),
.parity(),
.busy(kb_bsy),
.rdy(kb_rdy),
.error());
//xc2s200-5fg256
reg rdy;
always @(posedge kb_rdy or negedge reset_n)
if (~reset_n)
rdy <= 0;
else
rdy <= ~rdy;
assign {fpga_din_d0, fpga_d1, fpga_d2, fpga_d3,
fpga_d4, fpga_d5, fpga_d6, fpga_d7} =
{ kb_scancode[6], rdy, kb_scancode[5:0] };
endmodule // fpga

109
v/fpga3.v Normal file
View File

@ -0,0 +1,109 @@
// fpga2.v
// simple test module for scancode module
module fpga (clka,
clkb,
reset_n,
ps2_clk,
ps2_data,
vga_blue0,
vga_blue1,
vga_blue2,
vga_green0,
vga_green1,
vga_green2,
vga_red0,
vga_red1,
vga_red2,
vga_hsync_n,
vga_vsync_n,
fpga_din_d0,
fpga_d1,
fpga_d2,
fpga_d3,
fpga_d4,
fpga_d5,
fpga_d6,
fpga_d7
);
input clka; // 100mhz
input clkb; // 50mhz
input reset_n;
input ps2_clk, ps2_data;
output vga_blue0, vga_blue1, vga_blue2;
output vga_green0, vga_green1, vga_green2;
output vga_red0, vga_red1, vga_red2;
output vga_hsync_n, vga_vsync_n;
output fpga_din_d0, fpga_d1, fpga_d2, fpga_d3,
fpga_d4, fpga_d5, fpga_d6, fpga_d7;
//
wire hsync, vsync;
wire [8:0] pixel;
// signals to create a 25MHz clock from the 100MHz input clock
wire clk25;
reg [1:0] gray_cnt;
// clock divider by 4 to for a slower clock
// uses grey code for minimized logic
always @(posedge clka or negedge reset_n)
if (~reset_n)
gray_cnt <= 2'b00;
else
case (gray_cnt)
2'b00: gray_cnt <= 2'b01;
2'b01: gray_cnt <= 2'b11;
2'b11: gray_cnt <= 2'b10;
2'b10: gray_cnt <= 2'b00;
default: gray_cnt <= 2'b00;
endcase
// assign the clock that this entity runs off
assign clk25 = gray_cnt[1];
reg [7:0] kb_scancode;
reg kb_rdy;
wire [7:0] kb_ascii;
wire kb_release;
wire kb_ascii_rdy;
scancode_convert scancode_convert(.clock(clk25),
.reset_n(reset_n),
.scancode(kb_scancode),
.ascii(kb_ascii),
.key_up(kb_release),
.strobe_in(kb_rdy),
.strobe_out(kb_ascii_rdy));
//xc2s200-5fg256
reg [2:0] clk8;
always @(posedge clk25 or negedge reset_n)
if (~reset_n)
clk8 = 3'b111;
else
clk8 = clk8 + 1;
always @(posedge clk25 or negedge reset_n)
if (~reset_n)
kb_scancode = 0;
else
if (clk8 == 8'b111)
begin
kb_scancode = kb_scancode + 1;
kb_rdy = 1;
end
else
kb_rdy = 0;
assign {fpga_din_d0, fpga_d1, fpga_d2, fpga_d3,
fpga_d4, fpga_d5, fpga_d6, fpga_d7} = kb_ascii;
endmodule // fpga

208
v/programramdac.v Normal file
View File

@ -0,0 +1,208 @@
// programramdac.v
// hardcoded values for initialising the RAMDAC
module dac_data(addr, o);
input[2:0] addr;
output o;
reg [7:0] o;
// hard code initial control register programming values
// DAC(76543210)
always @(addr)
case (addr)
3'd0: o <= 8'b10000001; // Cmd reg A, high colour dual edged mode
3'd1: o <= 8'b00000000; // Pallette address reg gets $00
3'd2: o <= 8'b11111111; // Read mask reg gets $FF
3'd3: o <= 8'b00000010; // Pallette address reg gets $02
3'd4: o <= 8'b00000010; // Command reg B gets $02
3'd5: o <= 8'b00000000; // Pallette address reg gets $00
endcase // case(addr)
endmodule
module dac_rs(addr, o);
input[2:0] addr;
output o;
reg [2:0] o;
// RS(210)
always @(addr)
case (addr)
3'd0: o <= 3'b110; // RS gets Command reg A
3'd1: o <= 3'b000; // RS gets Pallette address reg
3'd2: o <= 3'b010; // RS gets Read mask reg
3'd3: o <= 3'b000; // RS gets Pallette address reg
3'd4: o <= 3'b010; // RS gets Command reg B
3'd5: o <= 3'b000; // RS gets Pallette address reg
endcase
endmodule
module programramdac(rstn, clk, start, done, WRn, RDn, RS, data);
input rstn;
input clk;
input start; // start signal
output done;
output WRn; // write line to ramdac
output RDn; // read line ot ramdac
input [2:0] RS; // register select lines to ramdac
inout [7:0] data; // data lines to ramdac
reg done, WRn;
// FSM states for the main mealy FSM
parameter [2:0]
stIdle = 3'd0,
stWrite = 3'd1,
stWrCycle = 3'd2,
stNextWrite = 3'd3;
reg [2:0] presState, nextState;
// initCnt controls write state
reg [2:0] initCnt;
reg increment;
// signals to create a 12.5MHz clock from the 50MHz input clock
wire divclk;
reg [1:0] gray_cnt;
// create signals so the data and RS lines can be used as tristate
// buffers. this is important as they share lines with the ethernet PHY
reg [7:0] prgData;
reg [2:0] prgRS;
reg latchData;
reg latchRS;
wire [7:0] theData;
wire [2:0] theRs;
// data and register select
dac_data arraydac(.addr(initCnt), .o(theData));
dac_rs arrayrs(.addr(initCnt), .o(theRS));
// clock divider by 4 to for a slower clock to avoid timing violations
// uses grey code for minimized logic
always @(posedge clk or rstn)
if (~rstn)
gray_cnt <= 2'b00;
else
case (gray_cnt)
2'b00: gray_cnt <= 2'b01;
2'b01: gray_cnt <= 2'b11;
2'b11: gray_cnt <= 2'b10;
2'b10: gray_cnt <= 2'b00;
default: gray_cnt <= 2'b00;
endcase
// assign the clock that this entity runs off
assign divclk = gray_cnt[1];
// read isn't needed, tie high
assign RDn = 1;
// main clocked process
always @(posedge divclk or rstn)
if (~rstn)
begin
presState <= stIdle;
initCnt <= 0;
end
else
if (divclk == 1)
begin
presState <= nextState;
if (increment)
if (initCnt < 5)
initCnt <= initCnt + 1;
else
initCnt <= 0;
end
// Main FSM process
always @(presState, start, initCnt)
begin
// default signals and outputs for each FSM state
// note that the latch data and rs signals are defaulted to 1,
// so are only 0 in the idle state
WRn <= 1;
increment <= 0;
prgData <= 0;
prgRS <= 3'b001;
latchData <= 1;
latchRS <= 1;
done <= 0;
case (presState)
stIdle:
begin
// wait for start signal from another process
if (start)
begin
nextState <= stWrite;
// setup for the first write to the RAMDAC for use
// by setting the register select lines and
// the data lines
prgRS <= theRS;
prgData <= theData;
end
else
begin
nextState <= stIdle;
latchData <= 0;
latchRS <=0;
end
end
stWrite:
begin
// hold the register select and data lines for
// the write cycle and set the active low write signal
nextState <= stWrCycle;
prgRS <= theRS;
prgData <= theData;
WRn <= 0;
end
stWrCycle:
begin
// continue if all 5 registers have been programmed
if (initCnt == 5)
begin
nextState <= stIdle;
done <= 1;
end
else
// continue writing to the registers
nextState <= stNextWrite;
// hold the data to be sure the hold times aren't violated
prgRS <= theRS;
prgData <= theData;
// increment initCnt to program the next register
increment <= 1;
end
stNextWrite:
begin
nextState <= stWrite;
// setup for the next write cycle
prgRS <= theRS;
prgData <= theData;
end
endcase;
// assign data and RS prgData and prgRS respectively when they
// need to be latched otherwise keep them at high impedance
// to create a tri state buffer
end // always @ (presState, start, initCnt)
assign data = latchData ? prgData : 8'bz;
assign RS = latchRS ? prgRS : 3'bz;
endmodule

160
v/ps2.v Normal file
View File

@ -0,0 +1,160 @@
// ps2.v
//
// Monitor the serial datastream and clock from a PS/2 keyboard
// and output a scancode for any key that is pressed.
//
// Notes:
//
// The clock from the PS/2 keyboard is used directly. It is sampled at
// the frequency of the main clock input; edges are extracted from the
// sample clock. The main clock must be substantially faster than
// the 10 KHz PS/2 clock - 200 KHz or more.
//
// The scancode is only valid when the ready signal is high. The scancode
// should be registered by an external circuit on the first clock edge
// after the ready signal goes high.
//
// The ready signal pulses only after the key is released.
//
// The error flag is set whenever the PS/2 clock stops pulsing and the
// PS/2 clock is either at a low level or less than 11 bits of serial
// data have been received (start + 8 data + parity + stop).
//
module ps2(clk, // main clock
rst_n, // asynchronous reset
ps2_clk, // clock from keyboard
ps2_data, // data from keyboard
scancode, // key scancode
parity, // parity bit for scancode
busy, // busy receiving scancode
rdy, // scancode ready pulse
error // error receiving scancode
);
input clk, rst_n;
input ps2_clk;
input ps2_data;
output [7:0] scancode;
output parity;
output busy;
output rdy;
output error;
parameter FREQ = 25000; // frequency of the main clock (KHz)
parameter PS2_FREQ = 10; // keyboard clock frequency (KHz)
parameter TIMEOUT = FREQ / PS2_FREQ; // ps2_clk quiet timeout
parameter [7:0] KEY_RELEASE = 8'b11110000; // sent when key is rel'd
reg [13:0] timer_r; // time since last PS/2 clock edge
wire [13:0] timer_x;
reg [3:0] bitcnt_r; // number of received scancode bits
wire [3:0] bitcnt_x;
reg [4:0] ps2_clk_r; // PS/2 clock sync / edge detect
wire [4:0] ps2_clk_x;
reg [9:0] sc_r; // scancode shift register
wire [9:0] sc_x;
reg keyrel_r; // set when key release received
wire keyrel_x;
reg rdy_r; // set when scancode ready
wire rdy_x;
reg error_r; // set when an error occurs
wire error_x;
wire ps2_clk_fall_edge; // on falling edge of PS/2 clock
wire ps2_clk_rise_edge; // on rising edge of PS/2 clock
wire ps2_clk_edge; // on either edge of PS/2 clock
wire ps2_clk_quiet; // when no edges on PS/2 clock for TIMEOUT
wire scancode_rdy; // when scancode has been received
// shift the level on the PS/2 clock into a shift register
assign ps2_clk_x = {ps2_clk_r[3:0], ps2_clk};
// look at the PS/2 clock levels stored in the shift register
// and find rising or falling edges
assign ps2_clk_fall_edge = ps2_clk_r[4:1] == 4'b1100;
assign ps2_clk_rise_edge = ps2_clk_r[4:1] == 4'b0011;
assign ps2_clk_edge = ps2_clk_fall_edge || ps2_clk_rise_edge;
// shift the keyboard scancode into the shift register on the
// falling edge of the PS/2 clock
assign sc_x = ps2_clk_fall_edge ? {ps2_data, sc_r[9:1]} : sc_r;
// clear the timer right after a PS/2 clock edge and
// then keep incrementing it until the next edge
assign timer_x = ps2_clk_edge ? 0 : (timer_r + 1);
// indicate when the PS/2 clock has stopped pulsing and
// is at a high level.
assign ps2_clk_quiet = timer_r == TIMEOUT && ps2_clk_r[1];
// increment bit counter on each falling edge of the PS/2 clock.
// reset the bit counter if the PS/2 clock stops pulsing or
// if there was an error receiving the scancode.
// otherwise, keep the bit counter unchanged.
assign bitcnt_x = ps2_clk_fall_edge ? (bitcnt_r + 1) :
(ps2_clk_quiet || error_r) ? 0 :
bitcnt_r;
// a scancode has been received if the bit counter is 11 and
// the PS/2 clock has stopped pulsing
assign scancode_rdy = bitcnt_r == 4'd11 && ps2_clk_quiet;
/*
// look for the scancode sent when the key is released
assign keyrel_x = (sc_r[7:0] == KEY_RELEASE && scancode_rdy) ? 1 :
(rdy_r || error_r) ? 0 :
keyrel_r;
// the scancode for the pressed key arrives after receiving
// the key-release scancode
assign rdy_x = keyrel_r && scancode_rdy;
*/
assign rdy_x = scancode_rdy;
// indicate an error if the clock is low for too long or
// if it stops pulsing in the middle of a scancode
assign error_x = (timer_r == TIMEOUT && ps2_clk_r[1] == 0) ||
(ps2_clk_quiet && bitcnt_r != 4'd11 && bitcnt_r != 4'd0) ?
1 : error_r;
// outputs
assign scancode = sc_r[7:0]; // scancode
assign parity = sc_r[8]; // parity bit for the scancode
assign busy = bitcnt_r != 4'd0; // busy when recv'ing scancode
assign rdy = rdy_r; // scancode ready flag
assign error = error_r; // error flag
// update the various registers
always @(posedge clk or negedge rst_n)
if (~rst_n)
begin
ps2_clk_r <= 5'b11111; // assume PS/2 clock has been high
sc_r <= 0;
keyrel_r <= 0;
rdy_r <= 0;
timer_r <= 0;
bitcnt_r <= 0;
error_r <= 0;
end
else
begin
ps2_clk_r <= ps2_clk_x;
sc_r <= sc_x;
keyrel_r <= keyrel_x;
rdy_r <= rdy_x;
timer_r <= timer_x;
bitcnt_r <= bitcnt_x;
error_r <= error_x;
end
endmodule

101
v/run.v Normal file
View File

@ -0,0 +1,101 @@
// run.v
`include "vga.v"
`timescale 1ns / 1ns
module test;
reg clk, reset_n;
wire [8:0] pixel;
wire blank_n;
wire hsync;
wire vsync;
reg ps2_clk;
reg ps2_data;
wire [7:0] led_data;
vga vga(.reset_n(reset_n),
.clock(clk25),
.pixel(pixel),
.blank_n(blank_n),
.hsync(hsync),
.vsync(vsync),
.ps2_clk(ps2_clk),
.ps2_data(ps2_data),
.led_data(led_data));
// clock divider by 4 to for a slower clock
// uses grey code for minimized logic
reg [1:0] gray_cnt;
always @(posedge clk or negedge reset_n)
if (~reset_n)
gray_cnt <= 2'b00;
else
case (gray_cnt)
2'b00: gray_cnt <= 2'b01;
2'b01: gray_cnt <= 2'b11;
2'b11: gray_cnt <= 2'b10;
2'b10: gray_cnt <= 2'b00;
default: gray_cnt <= 2'b00;
endcase
wire clk25;
assign clk25 = gray_cnt[1];
initial
begin
$timeformat(-9, 0, "ns", 7);
$dumpfile("vga.vcd");
// $dumpvars(0, test.vga);
$dumpvars(0, test);
end
initial
begin
clk = 0;
reset_n = 1;
ps2_clk <= 0;
ps2_data <= 0;
#1 begin
reset_n = 0;
end
#100 begin
reset_n = 1;
end
#400000
begin
vga.scancode_convert.strobe_out = 1;
vga.crt_data = 8'h41;
vga.scancode_convert.ascii = 8'h41;
end
#200 vga.scancode_convert.strobe_out = 0;
#400
begin
vga.scancode_convert.strobe_out = 1;
vga.crt_data = 8'h42;
vga.scancode_convert.ascii = 8'h42;
end
#200 vga.scancode_convert.strobe_out = 0;
// #100000 $finish;
// #500000 $finish;
// #1000000 $finish;
#20000000 $finish;
end
always
begin
#5 clk = 0;
#5 clk = 1;
end
endmodule

372
v/scancode.v Normal file
View File

@ -0,0 +1,372 @@
// scancode.v
//
// simple AT style keyboard scancode to ascii convertion
// keeps track of shift, capslock and control keys
// inputs scancodes and outputs ascii
//
`include "scancode_rom.v"
module scancode_convert(clock,
reset_n,
scancode,
ascii,
key_up,
strobe_in,
strobe_out);
input clock;
input reset_n;
input [7:0] scancode;
output reg [7:0] ascii;
output reg key_up;
input strobe_in;
output reg strobe_out;
// one-hot state machine states
parameter [2:0]
C_INIT = 3'd0,
C_IDLE = 3'd1,
C_KEYPRESS = 3'd2,
C_KEYRELEASE = 3'd3,
C_RELEASE = 3'd4,
C_HOLD = 3'd5;
//
reg [2:0] state, nextstate;
reg release_prefix;
reg release_prefix_set, release_prefix_clear;
reg shift;
reg shift_set, shift_clear;
reg ctrl;
reg ctrl_set, ctrl_clear;
reg capslock;
reg capslock_toggle;
reg strobe_out_set, strobe_out_clear;
reg key_up_set, key_up_clear;
reg [2:0] hold_count;
reg [6:0] sc;
wire [7:0] rom_data;
wire raise;
// convert scancodes (plus shift/control) into ascii
scancode_rom scancode_rom(.addr({raise,sc}),
.data(rom_data));
assign raise = shift | capslock | ctrl;
// internal state
always @(posedge clock or negedge reset_n)
if (~reset_n)
begin
shift <= 1'b0;
capslock <= 1'b0;
ctrl <= 1'b0;
release_prefix <= 1'b0;
strobe_out <= 1'b0;
key_up <= 1'b0;
hold_count <= 3'b0;
end
else
begin
if (shift_set)
shift <= 1'b1;
else
if (shift_clear)
shift <= 1'b0;
if (ctrl_set)
ctrl <= 1'b1;
else
if (ctrl_clear)
ctrl <= 1'b0;
if (capslock_toggle)
capslock <= ~capslock;
if (release_prefix_set)
release_prefix <= 1'b1;
else
if (release_prefix_clear)
release_prefix <= 1'b0;
if (strobe_out_set)
strobe_out <= 1'b1;
else
if (strobe_out_clear)
strobe_out <= 1'b0;
if (key_up_set)
key_up <= 1'b1;
else
if (key_up_clear)
key_up <= 1'b0;
//
if (state == C_HOLD)
hold_count <= hold_count + 1;
else
hold_count <= 3'd0;
end
// next state
always @(posedge clock or negedge reset_n)
if (~reset_n)
state <= C_INIT;
else
state <= nextstate;
always @(posedge clock)
if (state == C_IDLE && strobe_in)
sc <= scancode[6:0];
always @(posedge clock)
if (state == C_KEYPRESS || state == C_KEYRELEASE)
ascii <= ctrl ? (rom_data - 8'h40) : rom_data;
always @(state or strobe_in or scancode or release_prefix or shift or ctrl or rom_data or hold_count)
begin
shift_set = 1'b0;
shift_clear = 1'b0;
ctrl_set = 1'b0;
ctrl_clear = 1'b0;
capslock_toggle = 1'b0;
release_prefix_set = 1'b0;
release_prefix_clear = 1'b0;
strobe_out_set = 1'b0;
strobe_out_clear = 1'b0;
key_up_set = 1'b0;
key_up_clear = 1'b0;
case (state) // synthesis full_case
C_INIT:
begin
nextstate = C_IDLE;
end
C_IDLE:
begin
if (strobe_in)
begin
case (scancode)
8'hf0: /* release prefix */
begin
release_prefix_set = 1'b1;
nextstate = C_IDLE;
end
8'h58: /* caps lock */
begin
if (release_prefix)
capslock_toggle = 1'b1;
nextstate = C_RELEASE;
end
8'h12, /* left shift */
8'h59: /* right shift */
begin
if (release_prefix)
shift_clear = 1'b1;
else
shift_set = 1'b1;
nextstate = C_RELEASE;
end
8'h14: /* left ctrl */
begin
if (release_prefix)
ctrl_clear = 1'b1;
else
ctrl_set = 1'b1;
nextstate = C_RELEASE;
end
default:
nextstate = release_prefix ?
C_KEYRELEASE : C_KEYPRESS;
endcase
end
else
nextstate = C_IDLE;
end
C_KEYPRESS:
begin
strobe_out_set = 1'b1;
nextstate = C_RELEASE;
end
C_KEYRELEASE:
begin
`ifdef SEND_KEYUP
strobe_out_set = 1'b1;
`endif
strobe_out_clear = 1'b1;
key_up_set = 1'b1;
nextstate = C_RELEASE;
end
C_RELEASE:
begin
release_prefix_clear = 1'b1;
nextstate = C_HOLD;
end
C_HOLD:
begin
nextstate = C_HOLD;
if (hold_count == 3'd4)
begin
strobe_out_clear = 1'b1;
key_up_clear = 1'b1;
nextstate = C_IDLE;
end
end
endcase
end
endmodule
// ------------------
//`define TEST
`ifdef TEST
`timescale 1ns / 1ns
module test;
reg clk, reset_n;
reg [7:0] testcode;
wire [7:0] kb_ascii;
reg kb_rdy;
wire kb_release;
wire kb_ascii_rdy;
scancode_convert scancode_convert(.clock(clk),
.reset_n(reset_n),
.scancode(testcode),
.ascii(kb_ascii),
.key_up(kb_release),
.strobe_in(kb_rdy),
.strobe_out(kb_ascii_rdy));
initial
begin
$timeformat(-9, 0, "ns", 7);
$dumpfile("scancode.vcd");
$dumpvars(0, test.scancode_convert);
end
initial
begin
clk = 1'b0;
reset_n = 1'b0;
kb_rdy = 1'b0;
#200 begin
reset_n = 1'b1;
end
// press "a"
#10 begin kb_rdy = 1'b1; testcode = 8'h1c; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end
#80 kb_rdy = 1'b0;
// press "b"
#1000 begin kb_rdy = 1'b1; testcode = 8'h32; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h32; end
#80 kb_rdy = 1'b0;
// enter
#1000 begin kb_rdy = 1'b1; testcode = 8'h5a; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h5a; end
#80 kb_rdy = 1'b0;
// shift "a"
#1000 begin kb_rdy = 1'b1; testcode = 8'h12; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h12; end
#80 kb_rdy = 1'b0;
// press "c"
#1000 begin kb_rdy = 1'b1; testcode = 8'h21; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h21; end
#80 kb_rdy = 1'b0;
// ctrl "a"
#1000 begin kb_rdy = 1'b1; testcode = 8'h14; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h14; end
#80 kb_rdy = 1'b0;
// press "d"
#1000 begin kb_rdy = 1'b1; testcode = 8'h23; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end
#80 kb_rdy = 1'b0;
#1000 begin kb_rdy = 1'b1; testcode = 8'h23; end
#80 kb_rdy = 1'b0;
#5000 $finish;
end
always
begin
#20 clk = 1'b0;
#20 clk = 1'b1;
end
endmodule
`endif

252
v/scancode2.v Normal file
View File

@ -0,0 +1,252 @@
// scancode2.v
//
// simple AT style keyboard scancode to ascii convertion
// keeps track of shift, capslock and control keys
// inputs scancodes and outputs ascii
//
// implicit state machine version
//
`include "scancode_rom.v"
module scancode_convert(clock,
reset_n,
scancode,
ascii,
key_up,
strobe_in,
strobe_out);
input clock;
input reset_n;
input [7:0] scancode;
output reg [7:0] ascii;
output reg key_up;
input strobe_in;
output reg strobe_out;
reg release_prefix;
reg shift;
reg ctrl;
reg capslock;
reg [6:0] sc;
wire [7:0] rom_data;
wire raise;
scancode_rom scancode_rom(.addr({raise,sc}),
.data(rom_data));
assign raise = shift | capslock | ctrl;
always @(posedge clock or negedge reset_n)
if (~reset_n)
begin
shift <= 0;
ctrl <= 0;
capslock <= 0;
release_prefix <= 0;
key_up <= 0;
strobe_out <= 0;
end
else
begin
if (strobe_in)
begin
strobe_out <= 0;
sc = scancode[6:0];
case (scancode)
8'hf0: /* release prefix */
begin
@(posedge clock) release_prefix <= 1;
end
8'h58: /* caps lock */
begin
if (~release_prefix)
@(posedge clock) capslock = ~capslock;
end
8'h12, /* left shift */
8'h59: /* right shift */
begin
@(posedge clock) shift = release_prefix ? 0 : 1;
end
8'h14: /* left ctrl */
begin
@(posedge clock) ctrl = release_prefix ? 0 : 1;
end
default:
begin
if (release_prefix)
begin
@(posedge clock)
begin
ascii = ctrl ? (rom_data - 8'h40) : rom_data;
strobe_out <= 1;
end
@(posedge clock) strobe_out <= 1;
@(posedge clock)
begin
strobe_out <= 0;
release_prefix <= 0;
end
end
else
begin
@(posedge clock)
begin
/*
ascii = ctrl ? (rom_data - 8'h40) : rom_data;
strobe_out <= 1;
*/
key_up <= 1;
end
@(posedge clock) strobe_out <= 0;
@(posedge clock)
begin
strobe_out <= 0;
release_prefix <= 0;
end
end
end
endcase // case(scancode)
end // if (strobe_in)
end // else: !if(~reset_n)
endmodule
///*
// ------------------
`timescale 1ns / 1ns
module test;
reg clk, reset_n;
reg [7:0] testcode;
wire [7:0] kb_ascii;
reg kb_rdy;
wire kb_release;
wire kb_ascii_rdy;
scancode_convert scancode_convert(.clock(clk),
.reset_n(reset_n),
.scancode(testcode),
.ascii(kb_ascii),
.key_up(kb_release),
.strobe_in(kb_rdy),
.strobe_out(kb_ascii_rdy));
initial
begin
$timeformat(-9, 0, "ns", 7);
$dumpfile("scancode.vcd");
$dumpvars(0, test.scancode_convert);
end
initial
begin
clk = 0;
reset_n = 0;
kb_rdy = 0;
#200 begin
reset_n = 1;
end
// press "a"
#10 begin kb_rdy = 1; testcode = 8'h1c; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h1c; end
#80 kb_rdy = 0;
// press "b"
#1000 begin kb_rdy = 1; testcode = 8'h32; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h32; end
#80 kb_rdy = 0;
// enter
#1000 begin kb_rdy = 1; testcode = 8'h5a; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h5a; end
#80 kb_rdy = 0;
// shift "a"
#1000 begin kb_rdy = 1; testcode = 8'h12; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h1c; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h1c; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h12; end
#80 kb_rdy = 0;
// press "c"
#1000 begin kb_rdy = 1; testcode = 8'h21; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h21; end
#80 kb_rdy = 0;
// ctrl "a"
#1000 begin kb_rdy = 1; testcode = 8'h14; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h1c; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h1c; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h14; end
#80 kb_rdy = 0;
// press "d"
#1000 begin kb_rdy = 1; testcode = 8'h23; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'hf0; end
#80 kb_rdy = 0;
#1000 begin kb_rdy = 1; testcode = 8'h23; end
#80 kb_rdy = 0;
#5000 $finish;
end
always
begin
#40 clk = 0;
#40 clk = 1;
end
endmodule
//*/

132
v/scancode_rom.v Normal file
View File

@ -0,0 +1,132 @@
// scancode_rom.v
module scancode_rom(addr, data);
input [7:0] addr;
output reg [7:0] data;
always @addr
case (addr)
8'h0d: data <= 8'h07; /* TAB */
8'h0e: data <= "`";
8'h11: data <= 0; /* Left alt */
8'h12: data <= 0; /* Left shift */
8'h14: data <= 0; /* Left ctrl */
8'h15: data <= "q";
8'h16: data <= "1";
8'h1a: data <= "z";
8'h1b: data <= "s";
8'h1c: data <= 8'h61 /* "a" */;
8'h1d: data <= "w";
8'h1e: data <= "2";
8'h21: data <= "c";
8'h22: data <= "x";
8'h23: data <= "d";
8'h24: data <= "e";
8'h25: data <= "4";
8'h26: data <= "3";
8'h29: data <= " "; /* SPACE */
8'h2a: data <= "v";
8'h2b: data <= "f";
8'h2c: data <= "t";
8'h2d: data <= "r";
8'h2e: data <= "5";
8'h31: data <= "n";
8'h32: data <= "b";
8'h33: data <= "h";
8'h34: data <= "g";
8'h35: data <= "y";
8'h36: data <= "6";
8'h3a: data <= "m";
8'h3b: data <= "j";
8'h3c: data <= "u";
8'h3d: data <= "7";
8'h3e: data <= "8";
8'h41: data <= ",";
8'h42: data <= "k";
8'h43: data <= "i";
8'h44: data <= "o";
8'h45: data <= "0";
8'h46: data <= "9";
8'h49: data <= ".";
8'h4a: data <= "/";
8'h4b: data <= "l";
8'h4c: data <= ";";
8'h4d: data <= "p";
8'h4e: data <= "-";
8'h52: data <= "'";
8'h54: data <= "[";
8'h55: data <= "=";
8'h58: data <= 0; /* Caps lock */
8'h59: data <= 0; /* Right shift */
8'h5a: data <= 8'h0d; /* Enter */
8'h5b: data <= "]";
8'h5d: data <= 8'h5c; /* "\\" */
8'h66: data <= 8'h08; /* BKSP */
8'h76: data <= 8'h1b; /* ESC */
8'h8e: data <= "~";
8'h95: data <= "Q";
8'h96: data <= "!";
8'h9a: data <= "Z";
8'h9b: data <= "S";
8'h9c: data <= 8'h41 /* "A" */;
8'h9d: data <= "W";
8'h9e: data <= "2";
8'ha1: data <= "C";
8'ha2: data <= "X";
8'ha3: data <= "D";
8'ha4: data <= "E";
8'ha5: data <= "$";
8'ha6: data <= "#";
8'ha9: data <= " "; /* SPACE */
8'haa: data <= "V";
8'hab: data <= "F";
8'hac: data <= "T";
8'had: data <= "R";
8'hae: data <= "%";
8'hb1: data <= "N";
8'hb2: data <= "B";
8'hb3: data <= "H";
8'hb4: data <= "G";
8'hb5: data <= "Y";
8'hb6: data <= "^";
8'hba: data <= "M";
8'hbb: data <= "J";
8'hbc: data <= "U";
8'hbd: data <= "&";
8'hbe: data <= "*";
8'hc1: data <= "<";
8'hc2: data <= "K";
8'hc3: data <= "I";
8'hc4: data <= "O";
8'hc5: data <= ")";
8'hc6: data <= "(";
8'hc9: data <= ">";
8'hca: data <= "?";
8'hcb: data <= "L";
8'hcc: data <= ":";
8'hcd: data <= "P";
8'hce: data <= "-";
8'hd2: data <= "\"";
8'hd4: data <= "{";
8'hd5: data <= "+";
8'hda: data <= 8'h0d; /* Enter */
8'hdb: data <= "}";
8'hdd: data <= "|";
8'he6: data <= 8'h08; /* BKSP */
8'hf6: data <= 8'h1b; /* ESC */
default: data <= 0; /* All other keys are undefined */
endcase
endmodule

224
v/vga.v Normal file
View File

@ -0,0 +1,224 @@
// vga.v
// simple b&w 640x480 terminal with VGA output
`include "vgacore.v"
`include "crt.v"
`include "video_ram.v"
`include "char_rom.v"
`include "ps2.v"
`include "scancode.v"
module vga (reset_n, clock,
pixel, blank_n,
hsync, vsync,
ps2_clk,
ps2_data,
led_data);
input reset_n;
input clock;
output [8:0] pixel;
output blank_n;
output hsync;
output vsync;
input ps2_clk, ps2_data;
output [7:0] led_data;
wire startVGA;
wire resetVGA;
wire done;
wire clearing;
wire [9:0] hloc; // horiz timing, including sync & blanking
wire [9:0] vloc;
wire hblank; // set when in non-visable part of frame
wire vblank;
wire [6:0] hpos; // horiz position 0..79, during visable frame
wire [8:0] vpos; // vert line 0..480, during visable frame
reg [8:0] pixelData;
wire [11:0] vpos_times_80;
wire pixelclk;
reg [2:0] pclk;
wire charclk;
reg charload;
reg [7:0] pixel_hold;
reg crtclk;
wire ram_wclk;
wire ram_wslot;
wire [11:0] ram_addr_write, ram_addr_video, ram_addr_mux;
wire [7:0] ram_data, ram_data_out, rom_data_out;
wire ram_we_n;
wire [9:0] rom_addr;
reg [7:0] rom_addr_char;
char_rom char_rom(.addr(rom_addr),
.data(rom_data_out));
video_ram video_ram(.addr(ram_addr_mux),
.data_out(ram_data_out), .clk_r(charload/*charclk*/),
.data_in(ram_data), .clk_w(ram_wclk), .we_n(ram_we_n));
vgacore vgacore(.reset_n(reset_n), .clock(clock),
.hsync(hsync),
.vsync(vsync),
.hblank(hblank),
.vblank(vblank),
.enable(blank_n),
.hloc(hloc),
.vloc(vloc));
wire kb_rdy;
wire kb_bsy;
wire kb_release;
wire [7:0] kb_scancode;
wire [7:0] kb_ascii;
wire kb_ascii_rdy;
reg [7:0] crt_data;
reg insert_crt_data;
ps2 ps2(.clk(clock),
.rst_n(reset_n),
.ps2_clk(ps2_clk),
.ps2_data(ps2_data),
.scancode(kb_scancode),
.parity(),
.busy(kb_bsy),
.rdy(kb_rdy),
.error()
);
scancode_convert scancode_convert(.clock(clock),
.reset_n(reset_n),
.scancode(kb_scancode),
.ascii(kb_ascii),
.key_up(kb_release),
.strobe_in(kb_rdy),
.strobe_out(kb_ascii_rdy));
// debug - led output
assign led_data = { kb_ascii[6], kb_rdy, kb_ascii[5:0] };
always @(posedge clock or negedge reset_n)
if (~reset_n)
begin
crt_data <= 0;
insert_crt_data <= 0;
end
else
begin
if (kb_ascii_rdy)
begin
crt_data <= kb_ascii;
if (~kb_release)
insert_crt_data <= 1;
end
else
insert_crt_data <= 0;
end
// assign insert_crt_data = kb_ascii_rdy && ~kb_release;
wire [6:0] cursorh;
wire [5:0] cursorv;
wire cursor_match;
crt crt(.reset_n(reset_n),
.clock(crtclk),
.insert(insert_crt_data),
.done(done),
.data(crt_data),
.clearing(clearing),
.ram_addr(ram_addr_write),
.ram_data(ram_data),
.ram_we_n(ram_we_n),
.ram_wclk(ram_wclk),
.ram_wslot(ram_wslot),
.cursorh(cursorh),
.cursorv(cursorv));
// generate video ram address from (vpos/8) * 80 + hpos
assign vpos_times_80 = {vpos[8:3], 6'b0} + {2'b00, vpos[8:3], 4'b0};
assign ram_addr_video = vpos_times_80 + {5'b00000, hpos};
assign ram_addr_mux = ram_we_n ? ram_addr_video : ram_addr_write;
assign rom_addr = {rom_addr_char[6:0], vpos[2:0]};
assign cursor_match = (cursorh == hpos && cursorv == vpos[8:3]) ? 1 : 0;
// clock divider by 8 - assume 8x8 font
always @(posedge pixelclk or negedge reset_n)
if (~reset_n)
pclk = 3'b111;
else
pclk = (hblank || clearing) ? 3'b111 : pclk + 1;
assign charclk = ~pclk[2];
// crtclk runs at half speed
always @(posedge pixelclk or negedge reset_n)
if (~reset_n)
crtclk = 1'b0;
else
crtclk = ~crtclk;
// ram "writ slot" sits at 2nd half of charclk cycle
assign ram_wslot = ~pclk[1] & pclk[2];
// generate hpos, vpos (from hloc, vloc of vgacore)
assign hpos = hloc[9:3];
assign vpos = vloc[8:0];
// latch ram output to form rom address
always @(posedge charclk)
rom_addr_char <= ram_data_out;
/*
//temp-debug
//always @(posedge charclk) rom_addr_char <= {1'b0, hpos};
wire [7:0] hack;
assign hack = {1'b0,hpos} + 8'h30;
//assign hack = {1'b0,hpos};
always @(posedge charclk) rom_addr_char <= hack;
*/
// inhibit shift and load instead during last slot
always @(negedge pixelclk or negedge reset_n)
if (~reset_n)
charload = 0;
else
charload = pclk == 3'b111 ? 1 : 0;
// shift pixel data, one bit at a time (or load when at last slot)
always @(posedge pixelclk)
pixel_hold <= charload ?
(cursor_match ? 8'hff : rom_data_out) :
{ pixel_hold[6:0], 1'b0 };
// clock pixel data on pixel clock
always @(posedge pixelclk)
pixelData <= pixel_hold[7] ? 9'h1ff : 9'h000;
assign pixel = (hblank || vblank) ? 9'h000 : pixelData;
// reset & start VGA
assign startVGA = 1;
assign resetVGA = reset_n & startVGA;
// Provide 25MHz pixel clock
assign pixelclk = clock;
endmodule

153
v/vgacore.v Normal file
View File

@ -0,0 +1,153 @@
// vgacore.v
//
// Creates VGA timing signals to a monitor, currently for 60Hz @ 640 * 480
//
// To change the resolution or refresh rate, change the value of
// the constants and the generics to whatever is desired. Changing the
// resolution and/or refresh also means the clock speed may have to change,
// currently based off a 25MHz clock...
//
module vgacore(reset_n, clock,
hblank, vblank,
hsync, vsync,
enable,
hloc, vloc);
input reset_n;
input clock;
output hblank;
output vblank;
output hsync;
output vsync;
output enable;
reg hblank;
reg hsync, vsync;
reg enable;
output [9:0] hloc;
output [9:0] vloc;
parameter H_SIZE = 640;
parameter V_SIZE = 480;
// sync signals
//
// |<--- Active Region --->|<--------- Blanking Region ------->|
// | (Pixels) | |
// | | |
// | | |
// -----+---------- ... --------+------------- ------------+---
// | | | | | |
// | | |<--Front |<--Sync |<--Back |
// | | | Porch-->| Time-->| Porch-->|
//-- | | ------------ |
// | | |
// |<---------------------------- Period --------------------->|
//
// horizontal timing signals
`define H_PIXELS H_SIZE
`define H_FRONTPORCH 30 + (640 - H_SIZE) / 2
`define H_SYNCTIME 100
`define H_BACKPORCH 30 + (640 - H_SIZE) / 2
`define H_PERIOD `H_PIXELS+ `H_FRONTPORCH+ `H_SYNCTIME+ `H_BACKPORCH
// vertical timing signals
`define V_LINES V_SIZE
`define V_FRONTPORCH 10 + (480 - V_SIZE) / 2
`define V_SYNCTIME 2
`define V_BACKPORCH 32 + (480 - V_SIZE) / 2
`define V_PERIOD `V_SYNCTIME+ `V_LINES+ `V_FRONTPORCH+ `V_BACKPORCH
reg [10:0] hcnt; // horizontal pixel counter
reg [9:0] vcnt; // vertical line counter
// control the reset, increment and overflow of the horiz pixel count
always @(posedge clock or negedge reset_n)
// reset asynchronously clears horizontal counter
if (~reset_n)
hcnt <= 0;
else
// horiz. counter increments on rising edge of dot clock
// horiz. counter restarts after the horizontal period
if (hcnt < (`H_PERIOD - 1))
hcnt <= hcnt + 1;
else
hcnt <= 0;
// control the reset, increment and overflow of the vert line ctr,
// after every horiz line
// always @(negedge hsync or negedge reset_n)
always @(negedge hblank or negedge reset_n)
// reset asynchronously clears line counter
if (~reset_n)
vcnt <= 0;
else
// vert. line counter increments after every horiz. line
// vert. line counter rolls-over after max lines
if (vcnt < (`V_PERIOD - 1))
vcnt <= vcnt + 1;
else
vcnt <= 0;
// set the horizontal sync high time and low time
always @(posedge clock or negedge reset_n)
// reset asynchronously sets horizontal sync to inactive
if (~reset_n)
hsync <= 0;
else
// horizontal sync is recomputed on the rising edge of every dot clk
// horiz. sync is low to signal start of a new line
if (hcnt >= (`H_FRONTPORCH + `H_PIXELS) &&
hcnt < (`H_FRONTPORCH + `H_PIXELS + `H_SYNCTIME))
hsync <= 1;
else
hsync <= 0;
// set the vertical sync high time and low time
always @(posedge clock or negedge reset_n)
// reset asynchronously sets vertical sync to inactive
if (~reset_n)
vsync <= 0;
else
// vertical sync is recomputed at the end of every line of pixels
// vert. sync is low to signal start of a new frame
if (vcnt >= (`V_LINES + `V_FRONTPORCH) &&
vcnt < (`V_LINES + `V_FRONTPORCH + `V_SYNCTIME))
vsync <= 1;
else
vsync <= 0;
// blanking
// assign hblank = (hcnt >= `H_PIXELS) && (hcnt < `H_PERIOD);
assign vblank = (vcnt >= `V_LINES) && (vcnt < `V_PERIOD);
always @(posedge clock or negedge reset_n)
if (~reset_n)
hblank <= 0;
else
if (hcnt < `H_PIXELS)
hblank <= 0;
else
hblank <= 1;
// asserts the blanking signal (active low)
always @(posedge clock)
// if we are outside the visible range on the screen then blank
if (hcnt >= `H_PIXELS || vcnt >= `V_LINES)
enable <= 0;
else
enable <= 1;
assign hloc = hcnt[9:0];
assign vloc = vcnt[9:0];
endmodule

23
v/video_mem.v Normal file
View File

@ -0,0 +1,23 @@
// video_mem.v
/* 1kx32 static ram */
module video_ram(addr, data_in, data_out, ce_n, we_n);
input [10:0] addr;
input [7:0] data_in;
input ce_n, we_n;
output [7:0] data_out;
reg [7:0] ram [0:2047];
always @(negedge we_n)
begin
if (ce_n == 0)
ram[addr] = data_in;
end
assign data_out = ram[addr];
endmodule

32
v/video_ram.v Normal file
View File

@ -0,0 +1,32 @@
// video_mem.v
/* 2kx8 static sync ram */
module video_ram(addr, data_in, data_out, clk_r, clk_w, we_n);
input [11:0] addr;
input [7:0] data_in;
input clk_r, clk_w, we_n;
output [7:0] data_out;
reg [7:0] ram [0:2048];
reg [11:0] ram_addr_w;
reg [11:0] ram_addr_r;
always @(posedge clk_w)
begin
ram_addr_w <= addr;
if (we_n == 0)
ram[ram_addr_w] <= data_in;
end
always @(posedge clk_r)
begin
ram_addr_r <= addr;
end
assign data_out = ram[ram_addr_r];
// assign data_out = 8'h20 + addr[7:0];
endmodule

17
v/xsa-200.ucf Normal file
View File

@ -0,0 +1,17 @@
net clka loc=r8;
net clkb loc=b8;
net ps2_clk loc=f4;
net ps2_data loc=e1;
net vga_blue0 loc=h4;
net vga_blue1 loc=k3;
net vga_blue2 loc=l5;
net vga_green0 loc=h2;
net vga_green1 loc=k5;
net vga_green2 loc=r1;
net vga_red0 loc=j1;
net vga_red1 loc=m1;
net vga_red2 loc=t2;
net vga_hsync_n loc=k4;
net vga_vsync_n loc=k1;