写Verilog最重要的是心中要有电路图(这个是官方说法),我觉得也可以理解为你要能知道你自己写出来的Verilog代码能够综合出什么东西来。
以下部分是必做题:
实验一 选择器
实验二 译码器和编码器
实验三 加法器与ALU
实验六 移位寄存器及桶形移位器
实验七 状态机及键盘输入
前面三个都很简单,这里我给出做第六第七的思路和代码。
实验六 移位寄存器及桶形移位器
RTL代码
点击查看代码
module top(
input clk,
input rst,
input [7:0] in,
output [6:0] seg0, // 改为 7 位
output [6:0] seg1 // 改为 7 位
);
reg [7:0] tmp;
reg [6:0] di; // 7 位
reg [6:0] gao; // 7 位
always @(posedge clk or posedge rst) begin
if (rst) begin
tmp <= in;
end
else begin
tmp <= {tmp[4]^tmp[3]^tmp[2]^tmp[0], tmp[7:1]};
end
end
always @(*) begin
// 默认值
di = 7'b1111111;
gao = 7'b1111111;
// 低4位(个位)显示
case (tmp[3:0])
4'h0: di = 7'b1000000; // 0
4'h1: di = 7'b1111001; // 1
4'h2: di = 7'b0100100; // 2
4'h3: di = 7'b0110000; // 3
4'h4: di = 7'b0011001; // 4
4'h5: di = 7'b0010010; // 5
4'h6: di = 7'b0000010; // 6
4'h7: di = 7'b1111000; // 7
4'h8: di = 7'b0000000; // 8
4'h9: di = 7'b0010000; // 9
4'hA: di = 7'b0001000; // A
4'hB: di = 7'b0000011; // B
4'hC: di = 7'b1000110; // C
4'hD: di = 7'b0100001; // D
4'hE: di = 7'b0000110; // E
4'hF: di = 7'b0001110; // F
endcase
// 高4位(十位)显示
case (tmp[7:4])
4'h0: gao = 7'b1000000; // 0
4'h1: gao = 7'b1111001; // 1
4'h2: gao = 7'b0100100; // 2
4'h3: gao = 7'b0110000; // 3
4'h4: gao = 7'b0011001; // 4
4'h5: gao = 7'b0010010; // 5
4'h6: gao = 7'b0000010; // 6
4'h7: gao = 7'b1111000; // 7
4'h8: gao = 7'b0000000; // 8
4'h9: gao = 7'b0010000; // 9
4'hA: gao = 7'b0001000; // A
4'hB: gao = 7'b0000011; // B
4'hC: gao = 7'b1000110; // C
4'hD: gao = 7'b0100001; // D
4'hE: gao = 7'b0000110; // E
4'hF: gao = 7'b0001110; // F
endcase
end
assign seg0 = di; // 7位
assign seg1 = gao; // 7位
endmodule
仿真代码
点击查看代码
#include <nvboard.h>
#include <Vtop.h>
static TOP_NAME dut;
void nvboard_bind_all_pins(TOP_NAME* top);
int main() {
nvboard_bind_all_pins(&dut);
nvboard_init();
while(1) {
nvboard_update();
dut.eval();
}
}
管脚约束
点击查看代码
top=top
clk BTNR
rst BTNL
in (SW7, SW6, SW5, SW4, SW3, SW2, SW1, SW0)
seg0 (SEG0G,SEG0F,SEG0E,SEG0D,SEG0C,SEG0B,SEG0A)
seg1 (SEG1G,SEG1F,SEG1E,SEG1D,SEG1C,SEG1B,SEG1A)
之后make run 使用下面拨码开关先进行初始化in,然后点击左按钮复位后即可一直惦记右按钮进行随机数生成了。这里.v代码很简单,就不讲述了。
实验七 状态机及键盘输入
实验七我借鉴了example的键盘部分,但是我会讲讲代码理解。
管脚约束
点击查看代码
top=top
kbd_clk (PS2_CLK)
kbd_data (PS2_DAT)
seg0 (DEC0P, SEG0G, SEG0F, SEG0E, SEG0D, SEG0C, SEG0B, SEG0A)
seg1 (DEC1P, SEG1G, SEG1F, SEG1E, SEG1D, SEG1C, SEG1B, SEG1A)
seg2 (DEC2P, SEG2G, SEG2F, SEG2E, SEG2D, SEG2C, SEG2B, SEG2A)
seg3 (DEC3P, SEG3G, SEG3F, SEG3E, SEG3D, SEG3C, SEG3B, SEG3A)
seg4 (DEC4P, SEG4G, SEG4F, SEG4E, SEG4D, SEG4C, SEG4B, SEG4A)
seg5 (DEC5P, SEG5G, SEG5F, SEG5E, SEG5D, SEG5C, SEG5B, SEG5A)
seg6 (DEC6P, SEG6G, SEG6F, SEG6E, SEG6D, SEG6C, SEG6B, SEG6A)
seg7 (DEC7P, SEG7G, SEG7F, SEG7E, SEG7D, SEG7C, SEG7B, SEG7A)
仿真代码
点击查看代码
#include <nvboard.h>
#include <Vtop.h>
static TOP_NAME dut;
void nvboard_bind_all_pins(TOP_NAME* top);
static void single_cycle() {
dut.clk = 0; dut.eval();
dut.clk = 1; dut.eval();
}
static void reset(int n) {
dut.rst = 1;
while (n -- > 0) single_cycle();
dut.rst = 0;
}
int main() {
nvboard_bind_all_pins(&dut);
nvboard_init();
reset(10);
while(1) {
nvboard_update();
single_cycle();
}
}
RTL代码
- top.v
点击查看代码
module top (
input clk,
input rst,
input kbd_clk, kbd_data,
output [7:0] seg0,
output [7:0] seg1,
output [7:0] seg2,
output [7:0] seg3,
output [7:0] seg4,
output [7:0] seg5,
output [7:0] seg6,
output [7:0] seg7
);
/* ps2_keyboard interface signals */
wire [7:0] data;
wire ready, overflow;
wire nextdata_n = 1'b0;
ps2_keyboard inst(
.clk(clk),
.clrn(~rst),
.ps2_clk(kbd_clk),
.ps2_data(kbd_data),
.data(data),
.ready(ready),
.nextdata_n(nextdata_n),
.overflow(overflow)
);
reg seg_en;
reg [7:0] cnt;
reg [1:0] state;
reg [7:0] curdata;
reg [7:0] ascii;
key2ascii inst2(
.clk(clk),
.kbd_data(curdata),
.ascii(ascii)
);
bcd7seg bcd7seg0(.en(seg_en), .b(curdata[3:0]), .h(seg0)); // 扫描码低4位
bcd7seg bcd7seg1(.en(seg_en), .b(curdata[7:4]), .h(seg1)); // 扫描码高4位
bcd7seg bcd7seg2(.en(seg_en), .b(ascii[3:0]), .h(seg3)); // ASCII码低4位
bcd7seg bcd7seg3(.en(seg_en), .b(ascii[7:4]), .h(seg4)); // ASCII码高4位
bcd7seg bcd7seg6(.en(1), .b(cnt[3:0]), .h(seg6)); // 计数器低4位
bcd7seg bcd7seg7(.en(1), .b(cnt[7:4]), .h(seg7)); // 计数器高4位
always @(posedge clk) begin
if (rst == 0 && ready) begin // 复位无效且有新数据
$display("keyboard: %x", data);
if (state == 2'b00) begin
seg_en <= 1'b1; // 使能显示
cnt <= cnt + 8'b1; // 计数器递增
curdata <= data; // 保存当前扫描码
state <= 2'b01; // 进入下一个状态
end else if (state == 2'b01) begin
if (data == 8'hf0) begin // 检测到释放码
state <= 2'b10;
seg_en <= 1'b0; // 关闭显示
end
end else if (state == 2'b10) begin
state <= 2'b00; // 返回初始状态
end
end
end
//初始化
initial begin
seg_en = 1'b0;
cnt = 0;
state = 0;
end
assign seg2 = 8'b11111111;
assign seg5 = 8'b11111111;
endmodule
- ps2_keyboard.v
点击查看代码
module ps2_keyboard (
input clk, // 系统时钟
input clrn, // 低电平复位信号
input ps2_clk, // PS/2 时钟信号
input ps2_data, // PS/2 数据信号
input nextdata_n, // 低电平有效的"读取下一个数据"信号
output [7:0] data, // 输出的键盘扫描码
output reg ready, // 数据准备好信号
output reg overflow // FIFO 溢出标志
);
reg [9:0] buffer; // 存储接收到的10位PS/2数据帧
reg [7:0] fifo [7:0]; // 8个字节的FIFO缓冲区
reg [2:0] w_ptr, r_ptr; // FIFO写指针和读指针
reg [3:0] count; // 接收位计数器(0-10)
reg [2:0] ps2_clk_sync; // PS/2时钟同步寄存器
always @(posedge clk) begin
ps2_clk_sync <= {ps2_clk_sync[1:0], ps2_clk};
end
wire sampling = ps2_clk_sync[2] & ~ps2_clk_sync[1];
always @(posedge clk) begin
if (clrn == 0) begin
count <= 0;
w_ptr <= 0;
r_ptr <= 0;
overflow <= 0;
ready <= 0;
end else begin
if (ready) begin
if(nextdata_n == 1'b0)
begin
r_ptr <= r_ptr + 3'b1;
if (w_ptr == (r_ptr + 1'b1))
ready <= 1'b0;
end
end
if (sampling) begin
if (count == 4'd10) begin
if ((buffer[0] == 0) &&
(ps2_data) &&
(^buffer[9:1])) begin
fifo[w_ptr] <= buffer[8:1];
w_ptr <= w_ptr + 3'b1;
ready <= 1'b1;
overflow <= overflow | (r_ptr == (w_ptr + 3'b1));
$display("kbd scan code: %x", buffer[8:1]);
end
count <= 0;
end else begin
buffer[count] <= ps2_data;
count <= count + 3'b1;
end
end
end
end
assign data = fifo[r_ptr];
endmodule
- key2ascii.v
点击查看代码
module key2ascii (
input clk,
input [7:0] kbd_data,
output reg [7:0] ascii
);
always @(posedge clk) begin
case (kbd_data)
8'h1c: ascii <= 8'h61; // a
8'h32: ascii <= 8'h62; // b
8'h21: ascii <= 8'h63; // c
8'h23: ascii <= 8'h64; // d
8'h24: ascii <= 8'h65; // e
8'h2b: ascii <= 8'h66; // f
8'h34: ascii <= 8'h67; // g
8'h33: ascii <= 8'h68; // h
8'h43: ascii <= 8'h69; // i
8'h3b: ascii <= 8'h6a; // j
8'h42: ascii <= 8'h6b; // k
8'h4b: ascii <= 8'h6c; // l
8'h3a: ascii <= 8'h6d; // m
8'h31: ascii <= 8'h6e; // n
8'h44: ascii <= 8'h6f; // o
8'h4d: ascii <= 8'h70; // p
8'h15: ascii <= 8'h71; // q
8'h2d: ascii <= 8'h72; // r
8'h1b: ascii <= 8'h73; // s
8'h2c: ascii <= 8'h74; // t
8'h3c: ascii <= 8'h75; // u
8'h2a: ascii <= 8'h76; // v
8'h1d: ascii <= 8'h77; // w
8'h22: ascii <= 8'h78; // x
8'h35: ascii <= 8'h79; // y
8'h1a: ascii <= 8'h7a; // z
8'h45: ascii <= 8'h30; // 0
8'h16: ascii <= 8'h31; // 1
8'h1e: ascii <= 8'h32; // 2
8'h26: ascii <= 8'h33; // 3
8'h25: ascii <= 8'h34; // 4
8'h2e: ascii <= 8'h35; // 5
8'h36: ascii <= 8'h36; // 6
8'h3d: ascii <= 8'h37; // 7
8'h3e: ascii <= 8'h38; // 8
8'h46: ascii <= 8'h39; // 9
default: ascii <= 8'h00;
endcase
end
endmodule
- bcd7seg.v
点击查看代码
module bcd7seg(
input [3:0] b,
input en,
output reg [7:0] h
);
always @(*) begin
if (!en) begin
h = 8'b11111111;
end else case(b)
4'b0000: h = 8'b00000011;//0
4'b0001: h = 8'b10011111;//1
4'b0010: h = 8'b00100101;//2
4'b0011: h = 8'b00001101;//3
4'b0100: h = 8'b10011001;//4
4'b0101: h = 8'b01001001;//5
4'b0110: h = 8'b01000001;//6
4'b0111: h = 8'b00011111;//7
4'b1000: h = 8'b00000001;//8
4'b1001: h = 8'b00001001;//9
4'b1010: h = 8'b00010001;//A
4'b1011: h = 8'b11000001;//b
4'b1100: h = 8'b01100001;//C
4'b1101: h = 8'b10000101;//d
4'b1110: h = 8'b01100001;//E
4'b1111: h = 8'b01110001;//F
default: h = ~8'b11111111;//异常,全暗下来
endcase
end
endmodule
刨析一下代码: