将 E203 的 ICB 总线转换为 32 位 Wishbone 总线是构建基于蜂鸟 E203 RISC-V 内核的 MCU 系统的常见需求。你看到的 8 位转换器通常是针对特定低速外设(如 I2C、SPI、UART 控制器)设计的,这些外设内部寄存器宽度可能就是 8 位。你需要的是一个通用的 32 位 ICB 到 32 位 Wishbone 总线转换桥。
以下是详细的操作说明和原理:
核心原理
协议差异理解:
- ICB (Internal Chip Bus): 蜂鸟 E203 使用的轻量级、低延迟、顺序、非阻塞总线协议。核心特点是:
- 命令通道 (
icb_cmd_*) 和响应通道 (icb_rsp_*) 分离。
- 单次传输:一次命令对应一次响应。
- 支持非对齐访问(通过
icb_cmd_size 信号)。
- 无显式 burst 支持(但可通过多次请求模拟)。
- 信号简单:
valid, ready, addr, wdata, wmask (写掩码,实现字节使能), size, rdata, err。
- Wishbone (Classic Pipelined): 标准片上总线协议,更通用,支持多主设备(虽然转换桥通常只做从设备)。关键点:
- 地址/数据复用通道 (
ADR, DAT_I, DAT_O)。
- 握手信号:
CYC (总线周期), STB (选通), ACK (响应), ERR (错误)。
- 支持等待周期 (
ACK 未有效时 STB 保持)。
- 字节使能信号
SEL (明确指定哪些字节有效)。
- 读写信号
WE。
- 通常要求地址对齐(地址是数据宽度的整数倍,32位时地址低2位通常为0)。
转换桥的核心工作:
- 协议映射: 将 ICB 的请求/响应时序转换为 Wishbone 的 CYC/STB/ACK 时序。
- 信号转换:
icb_cmd_addr -> wb_adr (注意地址对齐要求,可能需要处理低2位)。
icb_cmd_wdata -> wb_dat_o (写数据)。
icb_cmd_wmask -> wb_sel (字节使能,4位 for 32-bit)。
icb_cmd_read (通常由 !icb_cmd_wr 或单独信号表示) -> wb_we (0 表示读,1 表示写)。
icb_cmd_valid 和 icb_cmd_ready -> 控制 wb_cyc, wb_stb 的产生和 wb_ack/wb_err 的等待。
wb_dat_i -> icb_rsp_rdata (读数据)。
wb_err -> icb_rsp_err (错误响应)。
- 处理非对齐访问 (可选但推荐): ICB 允许非对齐访问(如访问地址 0x1001 的 32 位数据)。Wishbone 外设通常要求对齐访问。转换桥可以:
- 方案A (推荐): 在桥内部将非对齐访问拆分成多个对齐的 Wishbone 访问(例如,一个非对齐 32 位读拆成两个 32 位读,然后拼接并可能进行符号/零扩展)。这增加了桥的复杂性,但对外设透明。
- 方案B: 要求 E203 内核配置为只发起对齐访问(检查 E203 配置)。这样桥可以直接传递地址。
- 方案C: 将非对齐访问视为错误 (
icb_rsp_err = 1)。最简单,但可能不符合软件预期。
- 处理
size: ICB 的 icb_cmd_size 指示访问大小(字节、半字、字)。Wishbone 主要靠 SEL 和地址低两位隐含。转换桥需要根据 size 正确设置 wb_sel。对于读操作,桥可能需要根据 size 和地址低两位对 Wishbone 返回的 32 位数据进行符号/零扩展或截断,以符合 ICB 规范。
- 时序控制 (状态机): 这是核心。一个典型的状态机有:
IDLE: 等待 ICB 有效命令 (icb_cmd_valid)。一旦有效,置 wb_cyc=1, wb_stb=1, 设置地址、数据(写)、wb_we、wb_sel,进入 WAIT_ACK 状态。
WAIT_ACK: 等待 wb_ack 或 wb_err。
- 如果
wb_ack=1:锁存 wb_dat_i (读) 或忽略 (写),置 icb_rsp_valid=1,icb_rsp_rdata = 锁存的数据 (读),icb_rsp_err=0。同时置 wb_cyc=0, wb_stb=0。当 icb_rsp_ready=1 时,返回 IDLE。
- 如果
wb_err=1:置 icb_rsp_valid=1, icb_rsp_err=1。同时置 wb_cyc=0, wb_stb=0。当 icb_rsp_ready=1 时,返回 IDLE。
- 如果
wb_ack=0 且 wb_err=0:保持 wb_stb=1,继续在 WAIT_ACK 等待(处理 Wishbone 从设备的等待周期)。
如何修改/实现 32 位转换桥
定位现有代码: 在 e203_hbirdv2 或你使用的 E203 仓库中,查找类似 e203_subsys_perips.v 或 e203_subsys_mems.v 的顶层文件,或者查找包含 icb 和 wb 的模块名(如 icb_to_wb, biu_icb2wb 等)。找到那个 8 位的转换器。
分析 8 位转换器: 理解它的状态机、信号映射和如何处理 size/wmask/sel。它很可能已经处理了核心的协议转换逻辑,只是位宽是 8 位。
关键修改点 (8位 -> 32位):
- 数据宽度: 将所有与数据 (
wdata, rdata) 相关的信号从 8 位 ([7:0]) 改为 32 位 ([31:0])。
input [31:0] icb_cmd_wdata -> output [31:0] wb_dat_o
output [31:0] icb_rsp_rdata <- input [31:0] wb_dat_i
- 字节使能 (SEL): ICB 使用写掩码
icb_cmd_wmask,Wishbone 使用 wb_sel。对于 32 位总线:
wb_sel 需要是 4 位 ([3:0]),每位对应一个字节 (位0: Byte0/LSB, 位1: Byte1, 位2: Byte2, 位3: Byte3/MSB)。
- 根据 ICB 的
icb_cmd_size 和 icb_cmd_addr[1:0] 计算 wb_sel。例如:
size=2'b10 (32-bit Word): wb_sel = 4'b1111 (总是,地址低2位应为00,或按00处理)。
size=2'b01 (16-bit Halfword): 如果 addr[1]=0 -> wb_sel=4'b0011 (低半字); 如果 addr[1]=1 -> wb_sel=4'b1100 (高半字)。(注意:地址对齐处理见下一点)
size=2'b00 (8-bit Byte): wb_sel = 4'b0001 << icb_cmd_addr[1:0] (例如 addr[1:0]=2'b01 -> sel=4'b0010)。
- 注意:
icb_cmd_wmask 在 ICB 端也是根据 size 和 addr[1:0] 生成的,逻辑与计算 wb_sel 类似。在桥内部,你可以选择使用 icb_cmd_wmask 直接作为 wb_sel 的基础(确保它是 4 位),或者根据 size 和 addr[1:0] 重新计算 wb_sel。两者本质相同。
- 地址处理: 这是关键修改!**
- 对齐问题: Wishbone 32 位从设备通常期望
wb_adr 的低 2 位 (adr[1:0]) 是 00。ICB 请求的地址 (icb_cmd_addr) 低 2 位可能不是 00。
- 解决方案 (必须实现其一):
- 方案A (内部拆分 - 复杂但通用): 在桥内部检测非对齐访问 (
icb_cmd_size > 2'b00 且 icb_cmd_addr[1:0] != 2'b00)。如果是非对齐访问:
- 将单个 ICB 请求拆分成 1个或2个 对齐的 Wishbone 访问。
- 例如,一个起始地址为
0x1001 的 32 位读 (size=10):
- 第一个 Wishbone 读:
adr = 0x1000, sel=4'b0011 (读低16位数据 [15:0] 到临时寄存器的高16位 [31:16])。
- 第二个 Wishbone 读:
adr = 0x1004, sel=4'b1100 (读高16位数据 [31:16] 到临时寄存器的低16位 [15:0])。(注意:地址是 0x1004 而不是 0x1004,因为 Wishbone 地址通常是字地址)
- 将两个读回的数据片段拼接成完整的 32 位数据 (
{data2[31:16], data1[15:0]}) 返回给 ICB。
- 写操作类似,需要拆分数据和
sel。
- 需要更复杂的状态机跟踪拆分过程,并合并响应。这会增加延迟和面积。
- 方案B (强制对齐 - 简单,依赖配置): 最简单常用的方法。 直接将
icb_cmd_addr 的高位 (例如 icb_cmd_addr[31:2]) 连接到 wb_adr,低 2 位 (icb_cmd_addr[1:0]) 丢弃。这相当于将 ICB 地址右移 2 位 (除以 4),得到字地址送给 Wishbone`。
- 前提: 必须确保 E203 内核配置为只发起对齐的内存访问! 检查 E203 的配置参数 (如
E203_ALIGNED_ACCES 或类似)。如果内核配置正确,icb_cmd_addr[1:0] 在访问 32/16 位数据时总是 00 (32位) 或 X0/0X (16位,低2位中只有1位可能非0,但传给 Wishbone 时丢弃低2位后地址仍是按字对齐的)。对于 8 位访问,地址任意,但 Wishbone 从设备通过 wb_sel 知道要访问哪个字节。
- 优点: 转换桥非常简单,只需要直连高位地址,状态机修改极小。
- 缺点: 完全依赖于 E203 发起对齐访问。如果内核配置允许非对齐访问或软件进行了非对齐访问,桥直接丢弃低2位会导致访问错误的 Wishbone 地址,行为是未定义的(通常是错误)。
wb_adr 宽度: 将 wb_adr 的宽度设置为目标 Wishbone 从设备所需的地址宽度。通常是 icb_cmd_addr 的高 N 位 (N = ICB_ADDR_WIDTH - 2),因为丢弃了低2位。例如,如果 icb_cmd_addr 是 32 位,wb_adr 通常是 30 位 ([31:2] -> [29:0] 或 [31:2] 直接作为 [29:0])。
状态机 (时序控制): 这部分逻辑在 8 位转换器中通常已经存在,并且与数据宽度无关。核心是 IDLE -> WAIT_ACK 的状态转移和握手信号的生成 (wb_cyc, wb_stb, wb_ack 等待, icb_rsp_valid 生成)。你通常不需要修改状态机本身,只需要确保它控制的信号宽度更新为 32 位 (如 rdata 锁存器)。
读数据处理 (符号/零扩展): 在 Wishbone 返回 32 位读数据 (wb_dat_i) 后,ICB 响应需要的数据宽度由原始的 icb_cmd_size 决定。桥需要:
- 根据
icb_cmd_size 和原始的 icb_cmd_addr[1:0] (即使你丢弃了它传给 wb_adr,在桥内部仍需保留用于数据处理) 从 wb_dat_i 中提取正确的字节/半字。
- 进行必要的符号扩展 (Signed Load) 或零扩展 (Unsigned Load)。这通常由另一个信号
icb_cmd_usign (无符号) 或 icb_cmd_signed (有符号) 控制,这个信号应该从 ICB 命令通道传来。
- 示例 (假设采用方案B,地址低2位已丢弃,但处理数据时需考虑原始低2位):
- 原始请求:
size=2'b00 (Byte), addr[1:0]=2'b01, usign=0 (有符号读)
wb_dat_i = 32'hAABBCCDD (从地址 addr[31:2] 读回)
- 需要的数据:地址
01 对应的字节是 CC (假设小端序)。
- 提取:
rdata_byte = wb_dat_i[15:8] (因为 addr[1:0]=01)。
- 符号扩展:因为是有符号读 (
usign=0),将 rdata_byte[7] 复制到 icb_rsp_rdata[31:8],icb_rsp_rdata[7:0] = rdata_byte[7:0] -> 结果 0xFFFFFFCC。
- 如果是无符号读 (
usign=1),则 icb_rsp_rdata = {24'b0, rdata_byte[7:0]} -> 0x000000CC。
总结步骤与建议
- 找到并理解 8位转换器: 分析其状态机和信号映射逻辑。
- 克隆并重命名: 复制一份 8 位转换器的 Verilog 文件,命名为类似
icb_to_wb_bridge_32bit.v 的名字。
- 修改位宽声明:
- 将所有
wdata/rdata 相关信号改为 [31:0]。
- 将
wb_sel 改为 [3:0]。
- 调整
wb_adr 宽度 (如 [AW-1:0],其中 AW = ICB_ADDR_WIDTH - 2)。
- 修改
wb_sel 生成逻辑: 根据 icb_cmd_size 和 icb_cmd_addr[1:0] 计算 4 位的 wb_sel (逻辑与之前 8 位转换器类似,但输出 4 位)。
- 实现地址处理 (关键!):
- 强烈推荐方案B (简单丢弃低2位):
assign wb_adr = icb_cmd_addr[31:2]; // 假设 ICB 地址32位
- 确保 E203 内核配置为生成对齐访问! 查找 E203 配置参数并设置。
- (如果必须支持非对齐且不想改内核配置,则实现方案A - 复杂拆分)
- 修改读数据处理逻辑: 在锁存
wb_dat_i 后,根据原始的 icb_cmd_size, icb_cmd_addr[1:0] 和 icb_cmd_usign (或类似信号) 进行字节/半字提取和符号/零扩展,生成正确的 32 位 icb_rsp_rdata。即使最终输出是 32 位,对于字节/半字访问,高位是扩展出来的。
- 检查状态机: 确认状态机逻辑本身不需要修改,它控制的是握手时序,与数据宽度无关。确保它控制的信号 (如锁存
rdata 的寄存器) 宽度已更新。
- 更新实例化: 在你系统的顶层模块中,用这个新的 32 位
icb_to_wb_bridge_32bit 替换掉原来的 8 位转换器(或者添加新的实例),连接到你的 32 位 Wishbone 外设(如 SDRAM 控制器、其他 IP 核)。
- 仿真测试: 编写测试台 (Testbench),模拟 E203 发起各种访问 (32位读写、16位读写高低地址、8位读写不同地址、对齐/非对齐(如果支持)),检查转换桥是否能正确产生 Wishbone 信号并返回正确的响应数据给 ICB。使用波形工具仔细调试。
简单 32 位转换桥核心代码示例 (方案B - 丢弃地址低2位)
module icb_to_wb_bridge_32bit #(
parameter ICB_ADDR_WIDTH = 32,
parameter WB_ADDR_WIDTH = ICB_ADDR_WIDTH - 2 // Wishbone 使用字地址
)(
// Clock & Reset
input wire clk,
input wire rst_n,
// ICB Slave Interface (E203 side)
input wire icb_cmd_valid,
output wire icb_cmd_ready,
input wire [ICB_ADDR_WIDTH-1:0] icb_cmd_addr,
input wire icb_cmd_read, // 1: read, 0: write
input wire [1:0] icb_cmd_size, // 00:1B, 01:2B, 10:4B
input wire [31:0] icb_cmd_wdata,
input wire [3:0] icb_cmd_wmask, // Byte write mask (derived from size & addr[1:0])
input wire icb_cmd_usign, // Unsigned load (for read)
output wire icb_rsp_valid,
input wire icb_rsp_ready,
output wire [31:0] icb_rsp_rdata,
output wire icb_rsp_err,
// Wishbone Master Interface (Peripheral side)
output wire wb_cyc_o,
output wire wb_stb_o,
input wire wb_ack_i,
input wire wb_err_i,
output wire [WB_ADDR_WIDTH-1:0] wb_adr_o,
output wire [3:0] wb_sel_o,
output wire wb_we_o,
output wire [31:0] wb_dat_o,
input wire [31:0] wb_dat_i
);
// State Definition
localparam S_IDLE = 1'b0;
localparam S_WAIT_ACK = 1'b1;
reg state;
// Registers
reg [31:0] rdata_reg;
reg err_reg;
reg [1:0] saved_size; // Save size for read data processing
reg [1:0] saved_addr_low; // Save addr[1:0] for read data processing
reg saved_usign; // Save usign for read data processing
// Internal Signals
wire cmd_fire = icb_cmd_valid & icb_cmd_ready;
wire rsp_fire = icb_rsp_valid & icb_rsp_ready;
// Wishbone Control Signals
assign wb_cyc_o = (state == S_WAIT_ACK);
assign wb_stb_o = (state == S_WAIT_ACK);
assign wb_we_o = ~icb_cmd_read; // Active High WE in Wishbone
assign wb_adr_o = icb_cmd_addr[ICB_ADDR_WIDTH-1:2]; // Drop low 2 bits (Word Aligned Address)
assign wb_dat_o = icb_cmd_wdata;
// Use the incoming wmask directly as wb_sel. It should be generated correctly upstream based on size and addr[1:0].
assign wb_sel_o = icb_cmd_wmask;
// ICB Response
assign icb_rsp_valid = (state == S_WAIT_ACK) & (wb_ack_i | wb_err_i);
assign icb_rsp_err = err_reg; // Set in WAIT_ACK on wb_err_i
assign icb_cmd_ready = (state == S_IDLE);
// Read Data Processing (Combinational)
always @(*) begin
icb_rsp_rdata = rdata_reg; // Default to full word
if (!wb_we_o) begin // Read operation
case (saved_size)
2'b00: begin // Byte
case (saved_addr_low)
2'b00: icb_rsp_rdata = saved_usign ? {24'h0, rdata_reg[7:0]} : {{24{rdata_reg[7]}}, rdata_reg[7:0]};
2'b01: icb_rsp_rdata = saved_usign ? {24'h0, rdata_reg[15:8]} : {{24{rdata_reg[15]}}, rdata_reg[15:8]};
2'b10: icb_rsp_rdata = saved_usign ? {24'h0, rdata_reg[23:16]} : {{24{rdata_reg[23]}}, rdata_reg[23:16]};
2'b11: icb_rsp_rdata = saved_usign ? {24'h0, rdata_reg[31:24]} : {{24{rdata_reg[31]}}, rdata_reg[31:24]};
endcase
end
2'b01: begin // Halfword
case (saved_addr_low[1])
1'b0: icb_rsp_rdata = saved_usign ? {16'h0, rdata_reg[15:0]} : {{16{rdata_reg[15]}}, rdata_reg[15:0]}; // Low Half @00 or 01 (discarded bit doesn't matter for selection now)
1'b1: icb_rsp_rdata = saved_usign ? {16'h0, rdata_reg[31:16]} : {{16{rdata_reg[31]}}, rdata_reg[31:16]}; // High Half @10 or 11
endcase
end
// 2'b10: Word - icb_rsp_rdata = rdata_reg (no change, default)
endcase
end
end
// State Machine
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE;
rdata_reg <= 32'b0;
err_reg <= 1'b0;
saved_size <= 2'b0;
saved_addr_low <= 2'b0;
saved_usign <= 1'b0;
end else begin
case (state)
S_IDLE: begin
if (cmd_fire) begin
// Save context for read data processing
saved_size <= icb_cmd_size;
saved_addr_low <= icb_cmd_addr[1:0]; // Save low 2 bits for data mux/extend
saved_usign <= icb_cmd_usign;
state <= S_WAIT_ACK;
end
end
S_WAIT_ACK: begin
if (wb_ack_i) begin
rdata_reg <= wb_dat_i; // Latch read data (even for write, harmless)
err_reg <= 1'b0;
state <= S_IDLE; // Will deassert wb_cyc/stb next cycle
end else if (wb_err_i) begin
err_reg <= 1'b1;
state <= S_IDLE; // Will deassert wb_cyc/stb next cycle
end
// Else stay in WAIT_ACK (waiting for ACK or ERR)
end
endcase
end
end
endmodule
重要说明:
- 地址对齐: 此示例采用方案B,直接丢弃地址低2位 (
wb_adr_o = icb_cmd_addr[ICB_ADDR_WIDTH-1:2])。你必须确保 E203 内核配置 (E203_HAS_ALIGN_FAULT 或类似) 被禁用,或者软件保证发起对齐访问。 如果内核可能发起非对齐访问,此桥会访问错误的 Wishbone 地址!
icb_cmd_wmask: 该示例假设上游 (通常是 E203 LSU) 已经根据 icb_cmd_size 和 icb_cmd_addr[1:0] 正确生成了 4 位的 icb_cmd_wmask,并直接将其用作 wb_sel_o。这是标准做法。
- 读数据处理: 状态机在
S_IDLE 捕获命令时保存了 size, addr[1:0] 和 usign。在 S_WAIT_ACK 收到 wb_ack_i 时锁存 wb_dat_i。icb_rsp_rdata 的输出是组合逻辑,根据保存的上下文对锁存的 32 位数据进行选择、符号/零扩展。
- 错误处理: 简单地将
wb_err_i 传递给 icb_rsp_err。
- 参数化: 使用参数
ICB_ADDR_WIDTH 和 WB_ADDR_WIDTH 提高灵活性。
- 时序: 这是一个同步设计,所有寄存器在
clk 上升沿更新。
最后建议
- 优先尝试方案B (丢弃地址低2位),并确认 E203 内核配置为对齐访问。这是最常见且实现最简单的方式。
- 仔细仿真。测试各种访问类型和地址边界情况。
- 参考 E203 仓库中其他总线转换模块 (如 AXI 转 ICB) 的设计思路。
- 如果遇到问题,查看转换桥的波形,重点关注
icb_cmd_*, icb_rsp_*, wb_* 信号在读写操作时的时序和数据值,特别是地址、sel、rdata 的扩展是否正确。
通过以上步骤和示例,你应该能够成功地将 E203 的 ICB 总线转换为 32 位 Wishbone 总线,用于连接你的 MCU 外设。
|
|
|
2025-11-11 18:10:17
评论
举报
|
|
|
|