数字电路基础实验

写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

刨析一下代码:

相关推荐
yuweijie01243 天前
一生一芯学习记录(一):简单介绍 + Verilator + Nvboard
一生一芯·verilator·nvboard
上课耽误学习2 个月前
一生一芯 PA2 RTFSC
开发语言·一生一芯
sucool_lb3 个月前
【一生一芯】数字实验七:状态机及键盘输入
一生一芯
sucool_lb3 个月前
《一生一芯》数字实验三:加法器与ALU
一生一芯
sucool_lb3 个月前
《一生一芯》数字实验六:实现随机数发生器
一生一芯
农民真快落1 年前
【IC设计】跨时钟异步处理系列——单比特跨时钟
fpga开发·verilog·ic设计·数字ic设计·一生一芯
农民真快落1 年前
【IC设计】牛客网-序列检测习题总结
fpga开发·verilog·数字ic设计·一生一芯·秋招面试
农民真快落1 年前
【IC设计】任意倍数占空比为50%的奇数分频和偶数分频(Verilog源码、仿真波形、讲解)
fpga开发·riscv·chisel·一生一芯·cpu设计
农民真快落1 年前
【程序人生】研二下学期快结束了~~~~随便写写
fpga开发·riscv·chisel·一生一芯·cpu设计