news 2026/6/11 6:29:56

简易寄存器接口SMMR

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
简易寄存器接口SMMR

参考

8位CPU设计n8_cpu.csdn

Zynq AXI-Lite 总线原理与实现.csdn

简易寄存器接口SMMR

SMMR,全称 Simple Memory-Mapped Register,是一种轻量的寄存器访问接口。方便各个 Verilog 模块以统一方式接入系统。
SMMR可以进一步简化 读写地址共用 比如 在8位CPU设计n8_cpu.csdn中的 bus.v 和 ram.v,缺点是读写不独立
和其他接口转换也会有限制。
本文有个 AXI-Lite和SMMR的桥接模块 axilite_slave_smmr_bridge,方便接入PS

SMMR的信号定义

因为是组合读,所以不用读使能

  • i_reg_wr_en: 写使能
  • i_reg_wr_addr:写寄存器地址
  • i_reg_wr_data:写入数据
  • i_reg_rd_addr:读寄存器地址
  • o_reg_rd_data:读出数据

其他接口

控制类、状态类、低速数据类接口都非常适合转换或包装成SMMR。
这样做的好处是:外设模块可以通过统一的寄存器读写方式接入系统,也方便单独编写 testbench 进行独立测试。

RPC 接口

input wire i_rpc_valid;output wire o_rpc_ready;input wire[7:0]i_rpc_cmd;input wire[31:0]i_rpc_arg0;input wire[31:0]i_rpc_arg1;output reg o_rpc_done;output reg[31:0]o_rpc_result;

FIFO 接口

input wire i_fifo_wr_en;input wire[31:0]i_fifo_wr_data;output wire o_fifo_full;input wire i_fifo_rd_en;output wire[31:0]o_fifo_rd_data;output wire o_fifo_empty;

IIC接口

// IIC 控制接口 output wire o_iic_exec;// 启动一次 IIC 操作 output wire o_iic_rw;//0: 写,1: 读 output wire[15:0]o_iic_addr;// IIC 器件内部地址 output wire[7:0]o_iic_wdata;// 写数据 input wire[7:0]i_iic_rdata;// 读数据 input wire i_iic_done;// 操作完成 input wire i_iic_ack;// ACK 状态 // IIC 物理接口 output wire o_iic_scl;inout wire io_iic_sda;

示例

顶层

top.v

`timescale 1ns/1ps //////////////////////////////////////////////////////////////////////////////// // 顶层模块 // // 本文件把整个寄存器访问链路连接起来,主要分为三层: // // 1. AXI-Lite 从接口 // 对外提供标准 AXI-Lite 访问接口。CPU、PS 或其他 AXI-Lite 主设备 // 可以通过这组信号读写本模块内部的寄存器。 // // 2. SMMR 简易寄存器总线 // SMMR 是 Simple Memory-Mapped Register 的缩写。 // AXI-Lite 协议会先被转换成项目内部更简单的寄存器读写信号: // - 写使能 // - 写地址 // - 写数据 // - 读地址 // - 读数据 // // 3. slot 外设模块 // SMMR slot 路由模块根据寄存器地址选择不同的 slot。 // 每个 slot 只需要实现简单的 SMMR 寄存器接口,不需要关心 // AXI-Lite 的握手时序。 // // 数据访问路径: // // AXI-Lite 主设备 // -> axilite_slave_smmr_bridge // -> smmr_slot_router // -> slot0 / slot1 // // 地址说明: // AXI-Lite 地址以字节为单位。 // slot 寄存器地址以 32 位寄存器为单位。 // smmr_slot_router 负责把字节地址转换成 32 位寄存器索引。 //////////////////////////////////////////////////////////////////////////////// module top #( // AXI-Lite 地址宽度,常见 FPGA SoC 系统中一般为 32 位。 parameter P_S_AXI_ADDR_WIDTH = 32, // AXI-Lite 数据宽度,本项目使用 32 位寄存器访问。 parameter P_S_AXI_DATA_WIDTH = 32, // AXI-Lite 可访问的寄存器窗口大小,单位是字节。 // 512 字节等价于 128 个 32 位寄存器。 parameter P_S_MEM_SIZE = 512, // 内部 SMMR 字节地址宽度。 // 9 位地址可以覆盖 512 字节空间。 parameter P_REG_ADDR_WIDTH = 9, // 系统时钟频率。 // 当前顶层主要把 AXI 时钟传给各级模块,此参数预留给需要时序计算的模块使用。 parameter P_CLK_FREQ = 50_000_000 )( // AXI-Lite 全局时钟和低有效复位。 input wire s_axi_aclk, input wire s_axi_aresetn, // AXI-Lite 写地址通道。 input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_awaddr, input wire s_axi_awvalid, output wire s_axi_awready, // AXI-Lite 写数据通道。 input wire [P_S_AXI_DATA_WIDTH-1:0] s_axi_wdata, input wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axi_wstrb, input wire s_axi_wvalid, output wire s_axi_wready, // AXI-Lite 写响应通道。 output wire [1:0] s_axi_bresp, output wire s_axi_bvalid, input wire s_axi_bready, // AXI-Lite 读地址通道。 input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_araddr, input wire s_axi_arvalid, output wire s_axi_arready, // AXI-Lite 读数据通道。 output wire [P_S_AXI_DATA_WIDTH-1:0] s_axi_rdata, output wire [1:0] s_axi_rresp, output wire s_axi_rvalid, input wire s_axi_rready, // 示例外设输出。 // 该信号由 slot1 内部的控制寄存器驱动。 output wire o_led ); //////////////////////////////////////////////////////////////////////////// // AXI-Lite 桥接模块到 SMMR 路由模块之间的信号 //////////////////////////////////////////////////////////////////////////// // AXI-Lite 写事务完成后产生的单周期写脉冲。 wire w_reg_wr_en; // SMMR 写地址和读地址。 // 这里仍然是字节地址,后面由 smmr_slot_router 转换成寄存器索引。 wire [P_REG_ADDR_WIDTH-1:0] w_reg_wr_addr; wire [P_REG_ADDR_WIDTH-1:0] w_reg_rd_addr; // SMMR 写数据和读数据。 wire [P_S_AXI_DATA_WIDTH-1:0] w_reg_wr_data; wire [P_S_AXI_DATA_WIDTH-1:0] w_reg_rd_data; //////////////////////////////////////////////////////////////////////////// // SMMR 路由模块到各个 slot 外设之间的信号 //////////////////////////////////////////////////////////////////////////// // slot 地址以 32 位寄存器为单位,因此比字节地址少 2 位。 // 例如字节地址 20 右移 2 位后变成寄存器地址 5。 wire [P_REG_ADDR_WIDTH-3:0] w_slot_wr_addr; wire [P_REG_ADDR_WIDTH-3:0] w_slot_rd_addr; // 写数据会同时送到所有 slot,真正写入哪个 slot 由对应写使能决定。 wire [P_S_AXI_DATA_WIDTH-1:0] w_slot_wr_data; // slot0 的写使能和读数据。 wire w_slot0_wr_en; wire [P_S_AXI_DATA_WIDTH-1:0] w_slot0_rd_data; // slot1 的写使能和读数据。 wire w_slot1_wr_en; wire [P_S_AXI_DATA_WIDTH-1:0] w_slot1_rd_data; //////////////////////////////////////////////////////////////////////////// // AXI-Lite 从接口转 SMMR 桥接模块 // // 该模块负责处理 AXI-Lite 的地址、数据、响应和握手信号。 // 对内部模块来说,它只输出简单的 SMMR 寄存器读写接口。 //////////////////////////////////////////////////////////////////////////// axilite_slave_smmr_bridge #( .P_S_AXI_ADDR_WIDTH(P_S_AXI_ADDR_WIDTH), .P_S_AXI_DATA_WIDTH(P_S_AXI_DATA_WIDTH), .P_S_MEM_SIZE (P_S_MEM_SIZE), .P_REG_ADDR_WIDTH (P_REG_ADDR_WIDTH) ) u_z8_axilite_slave ( .s_axi_aclk (s_axi_aclk), .s_axi_aresetn (s_axi_aresetn), .s_axi_awaddr (s_axi_awaddr), .s_axi_awvalid (s_axi_awvalid), .s_axi_awready (s_axi_awready), .s_axi_wdata (s_axi_wdata), .s_axi_wstrb (s_axi_wstrb), .s_axi_wvalid (s_axi_wvalid), .s_axi_wready (s_axi_wready), .s_axi_bresp (s_axi_bresp), .s_axi_bvalid (s_axi_bvalid), .s_axi_bready (s_axi_bready), .s_axi_araddr (s_axi_araddr), .s_axi_arvalid (s_axi_arvalid), .s_axi_arready (s_axi_arready), .s_axi_rdata (s_axi_rdata), .s_axi_rresp (s_axi_rresp), .s_axi_rvalid (s_axi_rvalid), .s_axi_rready (s_axi_rready), .o_reg_wr_en (w_reg_wr_en), .o_reg_wr_addr (w_reg_wr_addr), .o_reg_wr_data (w_reg_wr_data), .o_reg_rd_addr (w_reg_rd_addr), .i_reg_rd_data (w_reg_rd_data) ); //////////////////////////////////////////////////////////////////////////// // SMMR slot 路由模块 // // 该模块根据 SMMR 地址范围选择目标 slot。 // 写操作时,它给对应 slot 产生写使能。 // 读操作时,它把被选中 slot 的读数据返回给 AXI-Lite 桥接模块。 // // 当前地址映射: // slot0:寄存器地址 0 到 3 // slot1:寄存器地址 5 到 8 //////////////////////////////////////////////////////////////////////////// smmr_slot_router u_smmr_slot_hub ( .i_clk (s_axi_aclk), .i_rst_n (s_axi_aresetn), .i_reg_wr_en (w_reg_wr_en), .i_reg_wr_addr (w_reg_wr_addr), .i_reg_wr_data (w_reg_wr_data), .i_reg_rd_addr (w_reg_rd_addr), .o_reg_rd_data (w_reg_rd_data), .o_slot_wr_addr (w_slot_wr_addr), .o_slot_wr_data (w_slot_wr_data), .o_slot_rd_addr (w_slot_rd_addr), .o_slot0_wr_en (w_slot0_wr_en), .i_slot0_rd_data(w_slot0_rd_data), .o_slot1_wr_en (w_slot1_wr_en), .i_slot1_rd_data(w_slot1_rd_data) ); //////////////////////////////////////////////////////////////////////////// // slot0 外设 // // slot0 是一个简单寄存器块。 // 它可以作为新增 SMMR 外设的模板:只需要接入写使能、地址、写数据、 // 读地址和读数据,就可以挂到系统中。 //////////////////////////////////////////////////////////////////////////// slot0 u_slot0 ( .i_clk (s_axi_aclk), .i_rst_n (s_axi_aresetn), .i_reg_wr_en (w_slot0_wr_en), .i_reg_wr_addr (w_slot_wr_addr), .i_reg_wr_data (w_slot_wr_data), .i_reg_rd_addr (w_slot_rd_addr), .o_reg_rd_data (w_slot0_rd_data) ); //////////////////////////////////////////////////////////////////////////// // slot1 外设 // // slot1 也是一个 SMMR 寄存器块,同时多了一个实际硬件输出 o_led。 // 这展示了如何通过寄存器写入来控制 FPGA 内部或外部硬件信号。 //////////////////////////////////////////////////////////////////////////// slot1 u_slot1 ( .i_clk (s_axi_aclk), .i_rst_n (s_axi_aresetn), .i_reg_wr_en (w_slot1_wr_en), .i_reg_wr_addr (w_slot_wr_addr), .i_reg_wr_data (w_slot_wr_data), .i_reg_rd_addr (w_slot_rd_addr), .o_reg_rd_data (w_slot1_rd_data), .o_led (o_led) ); endmodule

AXI-Lite SMMR 桥接

axilite_slave_smmr_bridge.v

//AXI-Lite Slave 接口转 SMMR 简单寄存器接口 桥接模块 module axilite_slave_smmr_bridge #( parameter P_S_AXI_ADDR_WIDTH = 32, parameter P_S_AXI_DATA_WIDTH = 32, parameter P_S_MEM_SIZE = 512, // 内部存储器大小(单位:字节),对应128个32位寄存器 parameter P_REG_ADDR_WIDTH = 9 )( input wire s_axi_aclk, input wire s_axi_aresetn, input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_awaddr, input wire s_axi_awvalid, output reg s_axi_awready, input wire [P_S_AXI_DATA_WIDTH-1:0] s_axi_wdata, input wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axi_wstrb, input wire s_axi_wvalid, output reg s_axi_wready, output reg [1:0] s_axi_bresp, output reg s_axi_bvalid, input wire s_axi_bready, input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_araddr, input wire s_axi_arvalid, output reg s_axi_arready, output reg [P_S_AXI_DATA_WIDTH-1:0] s_axi_rdata, output reg [1:0] s_axi_rresp, output reg s_axi_rvalid, input wire s_axi_rready, // ========================= // Simple register bus // ========================= output reg o_reg_wr_en, output reg [P_REG_ADDR_WIDTH-1:0] o_reg_wr_addr, output reg [P_S_AXI_DATA_WIDTH-1:0] o_reg_wr_data, output reg [P_REG_ADDR_WIDTH-1:0] o_reg_rd_addr, input wire [P_S_AXI_DATA_WIDTH-1:0] i_reg_rd_data ); localparam S_IDLE = 3'd0; localparam S_WRITE_ADDR = 3'd1; localparam S_WRITE_DATA = 3'd2; localparam S_WRITE_RESP = 3'd3; localparam S_READ_ADDR = 3'd4; localparam S_READ_DATA = 3'd5; reg [2:0] r_state; reg [2:0] r_next_state; reg [P_S_AXI_ADDR_WIDTH-1:0] r_write_addr; reg [P_S_AXI_ADDR_WIDTH-1:0] r_read_addr; always @(posedge s_axi_aclk or negedge s_axi_aresetn) begin if (!s_axi_aresetn) begin r_state <= S_IDLE; end else begin r_state <= r_next_state; end end always @(*) begin r_next_state = r_state; case (r_state) S_IDLE: begin if (s_axi_awvalid) begin r_next_state = S_WRITE_ADDR; end else if (s_axi_arvalid) begin r_next_state = S_READ_ADDR; end end S_WRITE_ADDR: r_next_state = S_WRITE_DATA; S_WRITE_DATA: begin if (s_axi_wvalid) begin r_next_state = S_WRITE_RESP; end end S_WRITE_RESP: begin if (s_axi_bready) begin r_next_state = S_IDLE; end end S_READ_ADDR: r_next_state = S_READ_DATA; S_READ_DATA: begin if (s_axi_rready) begin r_next_state = S_IDLE; end end default: r_next_state = S_IDLE; endcase end always @(posedge s_axi_aclk or negedge s_axi_aresetn) begin if (!s_axi_aresetn) begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b0; s_axi_bresp <= 2'b00; s_axi_bvalid <= 1'b0; s_axi_arready <= 1'b0; s_axi_rdata <= {P_S_AXI_DATA_WIDTH{1'b0}}; s_axi_rresp <= 2'b00; s_axi_rvalid <= 1'b0; r_write_addr <= {P_S_AXI_ADDR_WIDTH{1'b0}}; r_read_addr <= {P_S_AXI_ADDR_WIDTH{1'b0}}; o_reg_wr_en <= 1'b0; o_reg_wr_addr <= {P_REG_ADDR_WIDTH{1'b0}}; o_reg_wr_data <= {P_S_AXI_DATA_WIDTH{1'b0}}; o_reg_rd_addr <= {P_REG_ADDR_WIDTH{1'b0}}; end else begin o_reg_wr_en <= 1'b0; case (r_state) S_IDLE: begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b0; s_axi_arready <= 1'b0; s_axi_rvalid <= 1'b0; if (s_axi_awvalid) begin s_axi_awready <= 1'b1; r_write_addr <= s_axi_awaddr; end else if (s_axi_arvalid) begin s_axi_arready <= 1'b1; r_read_addr <= s_axi_araddr; o_reg_rd_addr <= s_axi_araddr[P_REG_ADDR_WIDTH-1:0]; end end S_WRITE_ADDR: begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b1; end S_WRITE_DATA: begin if (s_axi_wvalid) begin s_axi_wready <= 1'b0; s_axi_bresp <= 2'b00; s_axi_bvalid <= 1'b1; o_reg_wr_en <= 1'b1; o_reg_wr_addr <= r_write_addr[P_REG_ADDR_WIDTH-1:0]; o_reg_wr_data <= s_axi_wdata; end end S_WRITE_RESP: begin if (s_axi_bready) begin s_axi_bvalid <= 1'b0; end end S_READ_ADDR: begin s_axi_arready <= 1'b0; s_axi_rdata <= i_reg_rd_data; s_axi_rresp <= 2'b00; s_axi_rvalid <= 1'b1; end S_READ_DATA: begin if (s_axi_rready) begin s_axi_rvalid <= 1'b0; end end default: begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b0; s_axi_arready <= 1'b0; s_axi_rvalid <= 1'b0; end endcase end end endmodule

SMMR 路由

smmr_slot_router.v

//////////////////////////////////////////////////////////////////////////////// // Simple Memory-Mapped Register 简易寄存器读写接口 // 1 路 smmr 转多路 smmr // u8地址 转 u32地址 //////////////////////////////////////////////////////////////////////////////// module smmr_slot_router #( //axi_lite 测的地址是9位的,slot的地址是7位的 parameter P_ADDR_WIDTH = 9, parameter P_DATA_WIDTH = 32, //SLOT地址范围 parameter P_SLOT0_BASE = 9'd0, parameter P_SLOT0_SIZE = 9'd4, parameter P_SLOT1_BASE = 9'd5, parameter P_SLOT1_SIZE = 9'd4 )( input wire i_clk, input wire i_rst_n, //axi_lite -> smmr_slot_hub //i_reg_wr_addr 和 i_reg_rd_addr 都是以u8为单位 input wire i_reg_wr_en, input wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr, input wire [P_DATA_WIDTH-1:0] i_reg_wr_data, input wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr, output reg [P_DATA_WIDTH-1:0] o_reg_rd_data, //简易寄存器读写接口 //o_slot_wr_addr 和 o_slot_rd_addr 都是以u32为单位 output wire [P_ADDR_WIDTH-3:0] o_slot_wr_addr, output wire [P_DATA_WIDTH-1:0] o_slot_wr_data, output wire [P_ADDR_WIDTH-3:0] o_slot_rd_addr, // slot0 output wire o_slot0_wr_en, input wire [P_DATA_WIDTH-1:0] i_slot0_rd_data, // slot1 output wire o_slot1_wr_en, input wire [P_DATA_WIDTH-1:0] i_slot1_rd_data ); wire [P_ADDR_WIDTH-3:0] w_reg_wr_addr_u32 = i_reg_wr_addr >> 2; wire [P_ADDR_WIDTH-3:0] w_reg_rd_addr_u32 = i_reg_rd_addr >> 2; reg r_slot0_wr_sel; reg r_slot1_wr_sel; reg r_slot0_rd_sel; reg r_slot1_rd_sel; always @(*) begin r_slot0_wr_sel = 1'b0; r_slot1_wr_sel = 1'b0; if ((w_reg_wr_addr_u32 >= P_SLOT0_BASE) && (w_reg_wr_addr_u32 < (P_SLOT0_BASE + P_SLOT0_SIZE))) begin r_slot0_wr_sel = 1'b1; end else if ((w_reg_wr_addr_u32 >= P_SLOT1_BASE) && (w_reg_wr_addr_u32 < (P_SLOT1_BASE + P_SLOT1_SIZE))) begin r_slot1_wr_sel = 1'b1; end end always @(*) begin r_slot0_rd_sel = 1'b0; r_slot1_rd_sel = 1'b0; if ((w_reg_rd_addr_u32 >= P_SLOT0_BASE) && (w_reg_rd_addr_u32 < (P_SLOT0_BASE + P_SLOT0_SIZE))) begin r_slot0_rd_sel = 1'b1; end else if ((w_reg_rd_addr_u32 >= P_SLOT1_BASE) && (w_reg_rd_addr_u32 < (P_SLOT1_BASE + P_SLOT1_SIZE))) begin r_slot1_rd_sel = 1'b1; end end assign o_slot0_wr_en = i_reg_wr_en && r_slot0_wr_sel; assign o_slot1_wr_en = i_reg_wr_en && r_slot1_wr_sel; assign o_slot_wr_addr = w_reg_wr_addr_u32; assign o_slot_wr_data = i_reg_wr_data; assign o_slot_rd_addr = w_reg_rd_addr_u32; always @(*) begin if (r_slot0_rd_sel) begin o_reg_rd_data = i_slot0_rd_data; end else if (r_slot1_rd_sel) begin o_reg_rd_data = i_slot1_rd_data; end else begin o_reg_rd_data = {P_DATA_WIDTH{1'b0}}; end end endmodule

slot0 外设

slot0.v

module slot0 #( parameter P_ADDR_WIDTH = 7, parameter P_DATA_WIDTH = 32, parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 0, parameter P_REG_COUNT = 4 )( input wire i_clk, input wire i_rst_n, input wire i_reg_wr_en, input wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr, input wire [P_DATA_WIDTH-1:0] i_reg_wr_data, input wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr, output reg [P_DATA_WIDTH-1:0] o_reg_rd_data ); reg [P_DATA_WIDTH-1:0] r_mem [0:P_REG_COUNT-1]; //写 integer i; always @(posedge i_clk or negedge i_rst_n) begin if (!i_rst_n) begin for (i = 0; i < P_REG_COUNT; i = i + 1) begin r_mem[i] <= {P_DATA_WIDTH{1'b0}}; end end else if (i_reg_wr_en && (i_reg_wr_addr < P_REG_COUNT)) begin r_mem[i_reg_wr_addr-P_SLOT_BASE] <= i_reg_wr_data; end end //读 always @(*) begin if (i_reg_rd_addr < P_SLOT_BASE+ P_REG_COUNT) begin o_reg_rd_data = r_mem[i_reg_rd_addr-P_SLOT_BASE]; end else begin o_reg_rd_data = {P_DATA_WIDTH{1'b0}}; end end endmodule

slot1 外设

slot1.v

module slot1 #( parameter P_ADDR_WIDTH = 7, parameter P_DATA_WIDTH = 32, parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 5, parameter P_REG_COUNT = 4 )( input wire i_clk, input wire i_rst_n, input wire i_reg_wr_en, input wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr, input wire [P_DATA_WIDTH-1:0] i_reg_wr_data, input wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr, output reg [P_DATA_WIDTH-1:0] o_reg_rd_data, output reg o_led ); // 控制寄存器:bit0:o_led状态 localparam [P_ADDR_WIDTH-1:0] ADDR_CONTROL = P_SLOT_BASE + 9'd0; reg [P_DATA_WIDTH-1:0] r_mem [0:P_REG_COUNT-1]; //写 integer i; always @(posedge i_clk or negedge i_rst_n) begin if (!i_rst_n) begin for (i = 0; i < P_REG_COUNT; i = i + 1) begin r_mem[i] <= {P_DATA_WIDTH{1'b0}}; end o_led<=0; end else if (i_reg_wr_en && (i_reg_wr_addr < P_REG_COUNT)) begin r_mem[i_reg_wr_addr-P_SLOT_BASE] <= i_reg_wr_data; case (i_reg_wr_addr) ADDR_CONTROL: begin o_led <= i_reg_wr_data[0]; end endcase end end //读 always @(*) begin if (i_reg_rd_addr < P_SLOT_BASE+ P_REG_COUNT) begin o_reg_rd_data = r_mem[i_reg_rd_addr-P_SLOT_BASE]; end else begin o_reg_rd_data = {P_DATA_WIDTH{1'b0}}; end end endmodule

slot1 外设单独测试

tb.sv

`timescale 1ns/1ps module tb; reg i_clk; reg i_rst_n; reg i_reg_wr_en; reg [6:0] i_reg_wr_addr; reg [31:0] i_reg_wr_data; reg [6:0] i_reg_rd_addr; wire [31:0] o_reg_rd_data; wire o_led; slot1#( .P_SLOT_BASE(0) ) u_slot1 ( .i_clk(i_clk), .i_rst_n(i_rst_n), .i_reg_wr_en(i_reg_wr_en), .i_reg_wr_addr(i_reg_wr_addr), .i_reg_wr_data(i_reg_wr_data), .i_reg_rd_addr(i_reg_rd_addr), .o_reg_rd_data(o_reg_rd_data), .o_led(o_led) ); initial begin i_clk = 1'b0; forever #10 i_clk = ~i_clk; end task reg_write; input [6:0] addr; input [31:0] data; begin @(posedge i_clk); i_reg_wr_addr <= addr; i_reg_wr_data <= data; @(posedge i_clk); i_reg_wr_en <= 1'b1; $display("[%0t ns] WRITE addr=0x%0h data=0x%08x", $time, addr, data); @(posedge i_clk); i_reg_wr_en <= 1'b0; i_reg_wr_addr <= 7'd0; i_reg_wr_data <= 32'd0; end endtask task reg_read; input [6:0] addr; begin @(posedge i_clk); i_reg_rd_addr <= addr; @(posedge i_clk); $display("[%0t ns] READ addr=0x%0h data=0x%08x o_led=%0b", $time, addr, o_reg_rd_data, o_led); end endtask task reset_dut; begin i_rst_n <= 1'b0; i_reg_wr_en <= 1'b0; i_reg_wr_addr <= 7'd0; i_reg_wr_data <= 32'd0; i_reg_rd_addr <= 7'd5; repeat (5) @(posedge i_clk); i_rst_n <= 1'b1; repeat (2) @(posedge i_clk); end endtask initial begin //复位寄存器 reset_dut; $display("==== slot1 reset read ===="); reg_read(7'd0); reg_read(7'd1); $display("==== slot1 write/read led on===="); reg_write(7'd0, 32'h0000_0001); reg_read (7'd0); reg_write(7'd1, 32'h12346789); reg_read (7'd1); $display("==== slot1 led off ===="); reg_write(7'd0, 32'h0000_0000); reg_read (7'd0); repeat (5) @(posedge i_clk); $finish; end endmodule

测试输出

# Bookmark "bookmark0" already exists with that definition in window "Wave", ignoring# Bookmark "bookmark1" already exists with that definition in window "Wave", ignoring# A bookmarks file was found in the current directory with 2 bookmarks# ==== slot1 reset read ====# [170000 ns] READ addr=0x0 data=0x00000000 o_led=0# [210000 ns] READ addr=0x1 data=0x00000000 o_led=0# ==== slot1 write/read led on====# [250000 ns] WRITE addr=0x0 data=0x00000001# [310000 ns] READ addr=0x0 data=0x00000001 o_led=1# [350000 ns] WRITE addr=0x1 data=0x12346789# [410000 ns] READ addr=0x1 data=0x12346789 o_led=1# ==== slot1 led off ====# [450000 ns] WRITE addr=0x0 data=0x00000000# [510000 ns] READ addr=0x0 data=0x00000000 o_led=0# ** Note: $finish : ../tb.sv(105)# Time: 610 ns Iteration: 1 Instance: /tb
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 6:21:52

Citra模拟器快速入门指南:10分钟解决黑屏闪退问题

Citra模拟器快速入门指南&#xff1a;10分钟解决黑屏闪退问题 【免费下载链接】citra A Nintendo 3DS Emulator 项目地址: https://gitcode.com/GitHub_Trending/ci/citra 你是否在使用Citra模拟器时遇到过令人沮丧的黑屏问题&#xff1f;游戏刚启动就闪退&#xff0c;或…

作者头像 李华
网站建设 2026/6/11 6:18:51

在Android 12上,用C++给RK3568写一个CAN总线通信库(附完整源码)

在Android 12上构建工业级RK3568 CAN总线通信库&#xff1a;从内核到应用的深度实践当RK3568遇上Android 12&#xff0c;这颗国产芯片的CAN控制器潜力才真正被释放。不同于简单的API调用教程&#xff0c;本文将带您深入Linux内核与用户空间的交界处&#xff0c;打造一个兼具实时…

作者头像 李华
网站建设 2026/6/11 6:14:03

MapLibre GL JS第42课:添加动态生成的图标

&#x1f4cc; 学习目标 掌握添加生成的图标的实现方法理解相关API的使用能够独立完成类似功能开发 &#x1f3af; 核心概念 向地图添加运行时生成的图标,也就是程序生产一个图标&#xff0c;动态生成的图标&#xff0c;作为要素图标。 &#x1f4bb; 完 整 代 码 代码示例…

作者头像 李华