reg_if的代码逻辑分析

`include "param_def.v" // 包含参数定义文件

module reg_if (
  clk_i,             // 时钟输入
  rst_n_i,           // 复位输入 (低电平有效)

  paddr_i,           // 外部总线地址输入
  pwr_i,             // 外部总线写使能输入
  pen_i,             // 外部总线写使能输入
  psel_i,            // 外部总线选择输入
  pwdata_i,          // 外部总线写数据输入

  prdata_o,          // 外部总线读数据输出
  pready_o,          // 外部总线就绪输出
  pslverr_o,         // 外部总线错误输出

  slv_en_o,          // 从设备使能输出 (4位向量)
  err_clr_o,         // 错误清除输出 (4位向量)
  slv0_id_o,         // 从设备0 ID 输出
  slv1_id_o,         // 从设备1 ID 输出
  slv2_id_o,         // 从设备2 ID 输出
  slv3_id_o,         // 从设备3 ID 输出

  slv0_len_o,        // 从设备0 数据长度输出
  slv1_len_o,        // 从设备1 数据长度输出
  slv2_len_o,        // 从设备2 数据长度输出
  slv3_len_o,        // 从设备3 数据长度输出

  slv0_parity_err_i, // 从设备0 奇偶校验错误输入
  slv1_parity_err_i, // 从设备1 奇偶校验错误输入
  slv2_parity_err_i, // 从设备2 奇偶校验错误输入
  slv3_parity_err_i, // 从设备3 奇偶校验错误输入

  slv0_free_slot_i,  // 从设备0 空闲槽位输入
  slv1_free_slot_i,  // 从设备1 空闲槽位输入
  slv2_free_slot_i,  // 从设备2 空闲槽位输入
  slv3_free_slot_i   // 从设备3 空闲槽位输入
);

input              clk_i;
input              rst_n_i;

input  [7:0]       paddr_i;
input              pwr_i;
input              pen_i;
input              psel_i;
input  [31:0]      pwdata_i;

output [31:0]      prdata_o;
output             pready_o;
output             pslverr_o;

output [3:0]       slv_en_o;
output [3:0]       err_clr_o;
output [7:0]       slv0_id_o;
output [7:0]       slv1_id_o;
output [7:0]       slv2_id_o;
output [7:0]       slv3_id_o;

output [7:0]       slv0_len_o;
output [7:0]       slv1_len_o;
output [7:0]       slv2_len_o;
output [7:0]       slv3_len_o;

input              slv0_parity_err_i;
input              slv1_parity_err_i;
input              slv2_parity_err_i;
input              slv3_parity_err_i;

input  [5:0]       slv0_free_slot_i;
input  [5:0]       slv1_free_slot_i;
input  [5:0]       slv2_free_slot_i;
input  [5:0]       slv3_free_slot_i;


parameter [1:0]     st_IDLE  =2'b00; // 空闲状态
parameter [1:0]     st_SETUP =2'b01; // 设置状态
parameter [1:0]     st_ACC   =2'b10; // 访问状态

reg [1:0] last_st, cur_st; // 上一个状态和当前状态

reg     [31:0]      ctrl_mem [3:0]; // 控制寄存器内存 (4个寄存器)
reg     [31:0]      ro_mem   [7:0]; // 只读寄存器内存 (8个寄存器)

wire                is_st_idle_s, is_st_setup_s, is_st_acc_s; // 状态标志
wire                is_addr_freeslot_3_s, is_addr_freeslot_2_s, is_addr_freeslot_1_s, is_addr_freeslot_0_s; // 地址解码标志 (空闲槽位)
wire                is_addr_parity_err_3_s, is_addr_parity_err_2_s, is_addr_parity_err_1_s, is_addr_parity_err_0_s; // 地址解码标志 (奇偶校验错误)
reg     [7:0]       addr_r; // 地址寄存器
reg     [31:0]      data_rd_r; // 读数据寄存器

wire                is_ctrl_rng_s, is_ro_rng_s, is_err_rng_s; // 地址范围标志
wire                idx_0_s, idx_1_s, idx_2_s, idx_3_s; // 地址索引标志
wire                is_addr_slv_en_s, is_addr_err_clr_s, is_addr_slv_id_s, is_addr_slv_len_s; // 地址解码标志 (控制寄存器)


//********************************************************************************************************
//     RTL Start...
//********************************************************************************************************

//--------------------------------------------------------------------------------------------------------
// 状态传递
//--------------------------------------------------------------------------------------------------------
always @(posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) last_st <= st_IDLE;
  else          last_st <= cur_st;
end

//--------------------------------------------------------------------------------------------------------
// 状态转移
//--------------------------------------------------------------------------------------------------------
always @(*) begin
  case (last_st)
    st_IDLE    : if (psel_i) cur_st <= st_SETUP; else cur_st <= st_IDLE;
    st_SETUP   : cur_st <= st_ACC;
    st_ACC     : if (psel_i && pen_i) cur_st <= st_ACC; else cur_st <= st_IDLE;
  endcase
end

// 状态标志赋值
assign is_st_idle_s  = (cur_st == st_IDLE ) ? 1'b1 : 1'b0;
assign is_st_setup_s = (cur_st == st_SETUP) ? 1'b1 : 1'b0;
assign is_st_acc_s   = (cur_st == st_ACC  ) ? 1'b1 : 1'b0;

// 地址寄存器赋值
always @(posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) addr_r <= 0;
  else if (is_st_setup_s) addr_r <= paddr_i;
end

// 读数据寄存器赋值,根据地址选择不同的寄存器
always @(*) begin
  data_rd_r <= 0;
  if (is_st_acc_s) begin
    if (!pwr_i) begin // 读操作
      if (is_addr_slv_en_s)   data_rd_r <= ctrl_mem[0];
      if (is_addr_err_clr_s)  data_rd_r <= ctrl_mem[1];
      if (is_addr_slv_id_s)   data_rd_r <= ctrl_mem[2];
      if (is_addr_slv_len_s)  data_rd_r <= ctrl_mem[3];
      if (is_addr_freeslot_0_s) data_rd_r <= ro_mem[0];
      if (is_addr_freeslot_1_s) data_rd_r <= ro_mem[1];
      if (is_addr_freeslot_2_s) data_rd_r <= ro_mem[2];
      if (is_addr_freeslot_3_s) data_rd_r <= ro_mem[3];
      if (is_addr_parity_err_0_s) data_rd_r <= ro_mem[4];
      if (is_addr_parity_err_1_s) data_rd_r <= ro_mem[5];
      if (is_addr_parity_err_2_s) data_rd_r <= ro_mem[6];
      if (is_addr_parity_err_3_s) data_rd_r <= ro_mem[7];
    end
  end
end

// 外部总线输出赋值
assign prdata_o  = data_rd_r;
assign pslverr_o = is_st_acc_s && is_err_rng_s;
assign pready_o  = is_st_acc_s;

//--------------------------------------------------------------------------------------------------------
// 地址译码器
//--------------------------------------------------------------------------------------------------------
// 地址范围判断
assign is_ctrl_rng_s = ~|(addr_r[7:4]); // 地址范围 0x00-0x0F (控制寄存器)
assign is_ro_rng_s   =  addr_r[7] && !addr_r[6] && !addr_r[5]; // 地址范围 0x80-0x9F (只读寄存器)
assign is_err_rng_s  =  ~(is_ctrl_rng_s | is_ro_rng_s); // 非法地址范围

// 地址索引判断
assign idx_0_s       = (addr_r[3:2]==2'b00)? 1'b1 : 1'b0;
assign idx_1_s       = (addr_r[3:2]==2'b01)? 1'b1 : 1'b0;
assign idx_2_s       = (addr_r[3:2]==2'b10)? 1'b1 : 1'b0;
assign idx_3_s       = (addr_r[3:2]==2'b11)? 1'b1 : 1'b0;

// 控制寄存器地址解码
assign is_addr_slv_en_s   = is_ctrl_rng_s & idx_0_s;
assign is_addr_err_clr_s  = is_ctrl_rng_s & idx_1_s;
assign is_addr_slv_id_s   = is_ctrl_rng_s & idx_2_s;
assign is_addr_slv_len_s  = is_ctrl_rng_s & idx_3_s;

// 只读寄存器地址解码 (空闲槽位和奇偶校验错误)
assign is_addr_freeslot_0_s  = is_ro_rng_s && !addr_r[4] && idx_0_s;
assign is_addr_freeslot_1_s  = is_ro_rng_s && !addr_r[4] && idx_1_s;
assign is_addr_freeslot_2_s  = is_ro_rng_s && !addr_r[4] && idx_2_s;
assign is_addr_freeslot_3_s  = is_ro_rng_s && !addr_r[4] && idx_3_s;
assign is_addr_parity_err_0_s  = is_ro_rng_s && addr_r[4] && idx_0_s;
assign is_addr_parity_err_1_s  = is_ro_rng_s && addr_r[4] && idx_1_s;
assign is_addr_parity_err_2_s  = is_ro_rng_s && addr_r[4] && idx_2_s;
assign is_addr_parity_err_3_s  = is_ro_rng_s && addr_r[4] && idx_3_s;

//--------------------------------------------------------------------------------------------------------
// 控制寄存器处理过程
//--------------------------------------------------------------------------------------------------------
always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    ctrl_mem[0] <= 32'h00000000; // slv_en 初始化
    ctrl_mem[1] <= 32'h00000000; // err_clr 初始化
    ctrl_mem[2] <= 32'h03020100; // slave ID 初始化
    ctrl_mem[3] <= 32'h00000000; // length 初始化
  end else begin
    if (is_st_acc_s & pwr_i) begin // 写操作
      if (is_addr_slv_en_s)   ctrl_mem[0][3:0] <= pwdata_i[3:0];
      if (is_addr_err_clr_s)  ctrl_mem[1][3:0] <= pwdata_i[3:0];
      if (is_addr_slv_id_s)   ctrl_mem[2]      <= pwdata_i;
      if (is_addr_slv_len_s)  ctrl_mem[3]      <= pwdata_i;
    end
  end
end

//--------------------------------------------------------------------------------------------------------
// 只读寄存器处理过程
//--------------------------------------------------------------------------------------------------------
always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    ro_mem[0] <= 32'h00000000; // slv0_free_slot 初始化
    ro_mem[1] <= 32'h00000000; // slv1_free_slot 初始化
    ro_mem[2] <= 32'h00000000; // slv2_free_slot 初始化
    ro_mem[3] <= 32'h00000000; // slv3_free_slot 初始化
    ro_mem[4] <= 32'h00000000; // slv0_parity_err 初始化
    ro_mem[5] <= 32'h00000000; // slv1_parity_err 初始化
    ro_mem[6] <= 32'h00000000; // slv2_parity_err 初始化
    ro_mem[7] <= 32'h00000000; // slv3_parity_err 初始化
  end else begin
    ro_mem[0][5:0] <= slv0_free_slot_i;
    ro_mem[1][5:0] <= slv1_free_slot_i;
    ro_mem[2][5:0] <= slv2_free_slot_i;
    ro_mem[3][5:0] <= slv3_free_slot_i;
    ro_mem[4][0]   <= slv0_parity_err_i;
    ro_mem[5][0]   <= slv1_parity_err_i;
    ro_mem[6][0]   <= slv2_parity_err_i;
    ro_mem[7][0]   <= slv3_parity_err_i;
  end
end

// 控制寄存器输出赋值
assign slv_en_o   = ctrl_mem[0][3:0];
assign err_clr_o  = ctrl_mem[1][3:0];
assign slv0_id_o  = ctrl_mem[2][7:0];
assign slv1_id_o  = ctrl_mem[2][15:8];
assign slv2_id_o  = ctrl_mem[2][23:16];
assign slv3_id_o  = ctrl_mem[2][31:24];
assign slv0_len_o = ctrl_mem[3][7:0];
assign slv1_len_o = ctrl_mem[3][15:8];
assign slv2_len_o = ctrl_mem[3][23:16];
assign slv3_len_o = ctrl_mem[3][31:24];

endmodule

这个模块(reg_if)充当 APB总线和四个从设备之间的接口。它管理从设备的控制寄存器和只读寄存器。该模块使用状态机来处理 AXI4-Lite 事务。地址解码逻辑根据输入地址 (paddr_i) 选择相应的寄存器。该模块还处理来自从设备的奇偶校验错误和空闲槽位报告。param_def.v 文件大概包含了该模块使用的参数定义。注释详细解释了每个信号的用途以及在每个状态和代码块中执行的操作。

always @(*) begin
  data_rd_r <= 0;
  if (is_st_acc_s) begin
    if (!pwr_i) begin // 读操作
      if (is_addr_slv_en_s)   data_rd_r <= ctrl_mem[0];
      if (is_addr_err_clr_s)  data_rd_r <= ctrl_mem[1];
      if (is_addr_slv_id_s)   data_rd_r <= ctrl_mem[2];
      if (is_addr_slv_len_s)  data_rd_r <= ctrl_mem[3];
      if (is_addr_freeslot_0_s) data_rd_r <= ro_mem[0];
      if (is_addr_freeslot_1_s) data_rd_r <= ro_mem[1];
      if (is_addr_freeslot_2_s) data_rd_r <= ro_mem[2];
      if (is_addr_freeslot_3_s) data_rd_r <= ro_mem[3];
      if (is_addr_parity_err_0_s) data_rd_r <= ro_mem[4];
      if (is_addr_parity_err_1_s) data_rd_r <= ro_mem[5];
      if (is_addr_parity_err_2_s) data_rd_r <= ro_mem[6];
      if (is_addr_parity_err_3_s) data_rd_r <= ro_mem[7];
    end
  end
end

这段代码是一个组合逻辑块,用于根据地址和状态机状态选择要读取的数据,并将其赋值给 data_rd_r 寄存器。让我们逐行分析:

always @(*) begin: 这行代码表示这是一个组合逻辑块,其输出值取决于输入值的组合,而不是时钟沿。这意味着只要任何输入信号发生变化,这个块就会重新计算输出值。

data_rd_r <= 0;: 这行代码将 data_rd_r 寄存器初始化为0。这是默认值,在没有匹配的地址或不处于读取状态时使用。

if (is_st_acc_s) begin: 这个条件语句检查当前状态机是否处于 st_ACC (访问) 状态。只有在这个状态下,才会执行后续的读取操作。

if (!pwr_i) begin // 读操作: 这个条件语句检查 pwr_i 信号是否为低电平。pwr_i 通常表示写使能信号,低电平表示进行读操作,高电平表示进行写操作。

if (is_addr_slv_en_s) data_rd_r <= ctrl_mem[0];: 这一系列 if 语句根据地址解码的结果选择要读取的数据。 is_addr_slv_en_s 等一系列变量表示地址是否匹配某个特定的寄存器。

is_addr_slv_en_s: 地址匹配从设备使能寄存器 (ctrl_mem[0])

is_addr_err_clr_s: 地址匹配错误清除寄存器 (ctrl_mem[1])

is_addr_slv_id_s: 地址匹配从设备ID寄存器 (ctrl_mem[2])

is_addr_slv_len_s: 地址匹配从设备长度寄存器 (ctrl_mem[3])

is_addr_freeslot_0_s 到 is_addr_freeslot_3_s: 地址匹配四个从设备的空闲槽位寄存器 (ro_mem[0] 到 ro_mem[3])

is_addr_parity_err_0_s 到 is_addr_parity_err_3_s: 地址匹配四个从设备的奇偶校验错误寄存器 (ro_mem[4] 到 ro_mem[7])

data_rd_r <= ctrl_mem[x]; or data_rd_r <= ro_mem[x];: 如果地址匹配,则将对应的寄存器值赋给 data_rd_r。 ctrl_mem 是控制寄存器数组,ro_mem 是只读寄存器数组。

end: 结束 if (!pwr_i) 块。

end: 结束 if (is_st_acc_s) 块。

end: 结束 always 块。

总而言之,这段代码实现了基于地址和状态的寄存器读取功能。它首先检查是否处于访问状态且是读操作,然后根据地址解码结果选择相应的寄存器,并将寄存器值赋值给 data_rd_r,最终作为模块的输出 prdata_o 提供给外部总线。 这种设计使用了组合逻辑,提高了响应速度,因为输出会立即反映输入的变化。 但是,需要注意的是,所有 is_addr_XXX_s 信号都必须在组合逻辑中定义,并且其值依赖于 addr_r (地址寄存器) 的值。


```c
// 外部总线输出赋值
assign prdata_o  = data_rd_r;
assign pslverr_o = is_st_acc_s && is_err_rng_s;
assign pready_o  = is_st_acc_s;

//--------------------------------------------------------------------------------------------------------
// 地址译码器
//--------------------------------------------------------------------------------------------------------
// 地址范围判断
assign is_ctrl_rng_s = ~|(addr_r[7:4]); // 地址范围 0x00-0x0F (控制寄存器)
assign is_ro_rng_s   =  addr_r[7] && !addr_r[6] && !addr_r[5]; // 地址范围 0x80-0x9F (只读寄存器)
assign is_err_rng_s  =  ~(is_ctrl_rng_s | is_ro_rng_s); // 非法地址范围

// 地址索引判断
assign idx_0_s       = (addr_r[3:2]==2'b00)? 1'b1 : 1'b0;
assign idx_1_s       = (addr_r[3:2]==2'b01)? 1'b1 : 1'b0;
assign idx_2_s       = (addr_r[3:2]==2'b10)? 1'b1 : 1'b0;
assign idx_3_s       = (addr_r[3:2]==2'b11)? 1'b1 : 1'b0;

// 控制寄存器地址解码
assign is_addr_slv_en_s   = is_ctrl_rng_s & idx_0_s;
assign is_addr_err_clr_s  = is_ctrl_rng_s & idx_1_s;
assign is_addr_slv_id_s   = is_ctrl_rng_s & idx_2_s;
assign is_addr_slv_len_s  = is_ctrl_rng_s & idx_3_s;请解释这段代码的含义
aThis code snippet implements address decoding and output assignment for an AXI4-Lite interface. Let’s break down each part:

**1. Output Ass_

a// 外部总线输出赋值
assign prdata_o  = data_rd_r;
assign pslverr_o = is_st_acc_s && is_err_rng_s;
assign pready_o  = is_st_acc_s;

//--------------------------------------------------------------------------------------------------------
// 地址译码器
//--------------------------------------------------------------------------------------------------------
// 地址范围判断
assign is_ctrl_rng_s = ~|(addr_r[7:4]); // 地址范围 0x00-0x0F (控制寄存器)
assign is_ro_rng_s   =  addr_r[7] && !addr_r[6] && !addr_r[5]; // 地址范围 0x80-0x9F (只读寄存器)
assign is_err_rng_s  =  ~(is_ctrl_rng_s | is_ro_rng_s); // 非法地址范围

// 地址索引判断
assign idx_0_s       = (addr_r[3:2]==2'b00)? 1'b1 : 1'b0;
assign idx_1_s       = (addr_r[3:2]==2'b01)? 1'b1 : 1'b0;
assign idx_2_s       = (addr_r[3:2]==2'b10)? 1'b1 : 1'b0;
assign idx_3_s       = (addr_r[3:2]==2'b11)? 1'b1 : 1'b0;

// 控制寄存器地址解码
assign is_addr_slv_en_s   = is_ctrl_rng_s & idx_0_s;
assign is_addr_err_clr_s  = is_ctrl_rng_s & idx_1_s;
assign is_addr_slv_id_s   = is_ctrl_rng_s & idx_2_s;
assign is_addr_slv_len_s  = is_ctrl_rng_s & idx_3_s;


这段代码实现了AXI4-Lite接口的地址译码和输出赋值。让我们逐段解释:

1. 外部总线输出赋值:

assign prdata_o = data_rd_r;: 将内部寄存器 data_rd_r 的值赋给 prdata_o,这是AXI4-Lite接口的数据输出。

assign pslverr_o = is_st_acc_s && is_err_rng_s;: 如果模块处于访问状态 (is_st_acc_s) 并且访问的地址无效 (is_err_rng_s),则将从设备错误输出 pslverr_o 赋值为1,否则为0。

assign pready_o = is_st_acc_s;: 当模块处于访问状态 (is_st_acc_s) 时,将就绪输出信号 pready_o 设置为高电平,表示它已准备好接收事务。

2. 地址译码器:

这部分代码对8位地址 addr_r 进行译码,以确定要访问哪个寄存器。

地址范围判断:

assign is_ctrl_rng_s = ~|(addr_r[7:4]);: 检查 addr_r 的高4位是否全为零 (0x00-0x0F)。 此范围用于控制寄存器。~| 是检查所有位是否为0的简写方式。

assign is_ro_rng_s = addr_r[7] && !addr_r[6] && !addr_r[5];: 检查地址是否在0x80-0x9F范围内 (仅使用位7、6和5)。 此范围用于只读寄存器。

assign is_err_rng_s = ~(is_ctrl_rng_s | is_ro_rng_s);: 检查地址是否超出定义的范围 (0x00-0x0F和0x80-0x9F),表示地址无效。

地址索引判断:

assign idx_0_s = (addr_r[3:2]==2'b00)? 1'b1 : 1'b0;: 这几行代码确定地址的低2位,用于选择控制和只读寄存器地址范围内的各个寄存器。 每行代表一个不同的寄存器索引 (0, 1, 2, 3)。

控制寄存器地址解码:

assign is_addr_slv_en_s = is_ctrl_rng_s & idx_0_s;: 这些行将范围检查和索引检查组合起来,生成指示地址是否匹配特定控制寄存器的信号。 例如,只有当地址在控制寄存器范围内且索引为0时,is_addr_slv_en_s 才为高电平。

总而言之,这段代码高效地解码了8位地址,以在两个不同的地址范围(控制寄存器和只读寄存器)内选择多个寄存器中的一个。它使用布尔逻辑创建信号来指示每个寄存器的匹配情况,这些信号随后用于选择要读取的适当数据或发出错误信号。该设计简洁且针对硬件实现进行了优化。

```c

```c
//--------------------------------------------------------------------------------------------------------
// 控制寄存器处理过程
//--------------------------------------------------------------------------------------------------------

```c
always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    ctrl_mem[0] <= 32'h00000000; // slv_en 初始化
    ctrl_mem[1] <= 32'h00000000; // err_clr 初始化
    ctrl_mem[2] <= 32'h03020100; // slave ID 初始化
    ctrl_mem[3] <= 32'h00000000; // length 初始化
  end else begin
    if (is_st_acc_s & pwr_i) begin // 写操作
      if (is_addr_slv_en_s)   ctrl_mem[0][3:0] <= pwdata_i[3:0];
      if (is_addr_err_clr_s)  ctrl_mem[1][3:0] <= pwdata_i[3:0];
      if (is_addr_slv_id_s)   ctrl_mem[2]      <= pwdata_i;
      if (is_addr_slv_len_s)  ctrl_mem[3]      <= pwdata_i;
    end
  end
end
//--------------------------------------------------------------------------------------------------------
// 只读寄存器处理过程
//--------------------------------------------------------------------------------------------------------
always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    ro_mem[0] <= 32'h00000000; // slv0_free_slot 初始化
    ro_mem[1] <= 32'h00000000; // slv1_free_slot 初始化
    ro_mem[2] <= 32'h00000000; // slv2_free_slot 初始化
    ro_mem[3] <= 32'h00000000; // slv3_free_slot 初始化
    ro_mem[4] <= 32'h00000000; // slv0_parity_err 初始化
    ro_mem[5] <= 32'h00000000; // slv1_parity_err 初始化
    ro_mem[6] <= 32'h00000000; // slv2_parity_err 初始化
    ro_mem[7] <= 32'h00000000; // slv3_parity_err 初始化
  end else begin
    ro_mem[0][5:0] <= slv0_free_slot_i;
    ro_mem[1][5:0] <= slv1_free_slot_i;
    ro_mem[2][5:0] <= slv2_free_slot_i;
    ro_mem[3][5:0] <= slv3_free_slot_i;
    ro_mem[4][0]   <= slv0_parity_err_i;
    ro_mem[5][0]   <= slv1_parity_err_i;
    ro_mem[6][0]   <= slv2_parity_err_i;
    ro_mem[7][0]   <= slv3_parity_err_i;
  end
end

// 控制寄存器输出赋值
assign slv_en_o   = ctrl_mem[0][3:0];
assign err_clr_o  = ctrl_mem[1][3:0];
assign slv0_id_o  = ctrl_mem[2][7:0];
assign slv1_id_o  = ctrl_mem[2][15:8];
assign slv2_id_o  = ctrl_mem[2][23:16];
assign slv3_id_o  = ctrl_mem[2][31:24];
assign slv0_len_o = ctrl_mem[3][7:0];
assign slv1_len_o = ctrl_mem[3][15:8];
assign slv2_len_o = ctrl_mem[3][23:16];
assign slv3_len_o = ctrl_mem[3][31:24];

这段代码描述了控制寄存器和只读寄存器的处理过程,以及控制寄存器输出的赋值。让我们分段解释:

  1. 控制寄存器处理过程:
always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    ctrl_mem[0] <= 32'h00000000; // slv_en 初始化
    ctrl_mem[1] <= 32'h00000000; // err_clr 初始化
    ctrl_mem[2] <= 32'h03020100; // slave ID 初始化
    ctrl_mem[3] <= 32'h00000000; // length 初始化
  end else begin
    if (is_st_acc_s & pwr_i) begin // 写操作
      if (is_addr_slv_en_s)   ctrl_mem[0][3:0] <= pwdata_i[3:0];
      if (is_addr_err_clr_s)  ctrl_mem[1][3:0] <= pwdata_i[3:0];
      if (is_addr_slv_id_s)   ctrl_mem[2]      <= pwdata_i;
      if (is_addr_slv_len_s)  ctrl_mem[3]      <= pwdata_i;
    end
  end
end

这段代码是一个时序逻辑块,它在时钟上升沿或复位信号下降沿触发。它描述了对控制寄存器 (ctrl_mem) 的处理:

复位操作: 如果 rst_n_i 为低电平(复位),则控制寄存器被初始化为特定的值。slv_en、err_clr 和 length 初始化为0,slave ID 初始化为 0x03020100 (这可能是四个从设备的ID)。

写操作: 如果模块处于访问状态 (is_st_acc_s) 并且 pwr_i 为高电平(写使能),则根据地址解码的结果,更新相应的控制寄存器。 只有当地址匹配对应的寄存器时,才会进行写入操作。例如,is_addr_slv_en_s 为真时,才更新 ctrl_mem[0] 的低4位。

  1. 只读寄存器处理过程:
always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    // 初始化 ro_mem 数组,每个元素代表一个从设备的空闲槽位或奇偶校验错误状态
  end else begin
    // 将从设备提供的空闲槽位和奇偶校验错误信息更新到 ro_mem 数组
  end
end

这段代码也是一个时序逻辑块,它在时钟上升沿或复位信号下降沿触发。它描述了对只读寄存器 (ro_mem) 的处理:

复位操作: 如果 rst_n_i 为低电平(复位),则所有只读寄存器被初始化为0。

更新操作: 在非复位状态下,这段代码将从外部输入 slv0_free_slot_i 到 slv3_free_slot_i (从设备0到3的空闲槽位) 和 slv0_parity_err_i 到 slv3_parity_err_i (从设备0到3的奇偶校验错误) 的值更新到 ro_mem 数组中对应的寄存器。

  1. 控制寄存器输出赋值:
assign slv_en_o   = ctrl_mem[0][3:0];
assign err_clr_o  = ctrl_mem[1][3:0];
assign slv0_id_o  = ctrl_mem[2][7:0];
assign slv1_id_o  = ctrl_mem[2][15:8];
assign slv2_id_o  = ctrl_mem[2][23:16];
assign slv3_id_o  = ctrl_mem[2][31:24];
assign slv0_len_o = ctrl_mem[3][7:0];
assign slv1_len_o = ctrl_mem[3][15:8];
assign slv2_len_o = ctrl_mem[3][23:16];
assign slv3_len_o = ctrl_mem[3][31:24];

这部分代码是组合逻辑,它将 ctrl_mem 数组中的值赋给相应的输出端口。 ctrl_mem[2] 和 ctrl_mem[3] 中包含四个从设备的ID和长度信息,这段代码将其分解成独立的输出信号。

总而言之,这段代码实现了对控制寄存器和只读寄存器的读写和更新操作,并提供相应的输出接口,用于与四个从设备进行交互。 它清晰地划分了控制寄存器和只读寄存器的处理逻辑,并利用组合逻辑高效地将内部寄存器值输出到外部接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值