Rocket Core与MMIO设备间Tilelink协议握手异常求助
看起来你在Tilelink协议握手的处理上踩了几个典型的坑,我帮你拆解下问题根源和修复方案:
一、先理清错误的直接原因
你遇到的Assertion failed: 'D' channel acknowledged for nothing inflight,是Tilelink的协议监视器检测到了违规的D通道响应:你的Slave在没有待响应的请求时,错误地发送了D通道消息,违反了Tilelink的协议规范。
二、逐个排查并修复问题
1. 测试程序的低级错误
你的reg_read64调用参数传递完全错误,导致read操作既没正确触发也没保存返回值:
// 原错误代码 reg_read64(MMIO_BASE_ADDR , slave_input_data); // 修复后代码 slave_input_data = reg_read64(MMIO_BASE_ADDR); printf("[CORE] Received data from slave: 0x%lx\n", slave_input_data);
reg_read64的定义只需要一个地址参数,你多传的slave_input_data是无效的,而且要把返回值赋值给变量才能看到结果。
2. Tilelink协议处理的核心逻辑错误
这是问题的重灾区,我分点说明:
(1)混淆了「需要响应/不需要响应」的请求类型
Tilelink v1中,PutFullData/PutPartialData(OP_PUT=000)是单向请求,Slave处理完后不需要返回D通道响应;只有Get、Arithmetic、Logical这类请求才需要返回D通道响应。你之前的代码错误地给Put请求发送了D响应,这直接触发了协议监视器的断言。
(2)完全遗漏了Get请求的处理逻辑
你的Slave代码只处理了OP_PUT的A通道请求,但测试程序里明确发起了Get请求(对应read操作),这会导致Get请求一直挂起,同时之前错误的D响应又和它无关,双重违规。
(3)D通道Opcode和Source ID赋值错误
- 你用了A通道的
OP_PUT=000作为D通道的Opcode,这是完全错误的:D通道有自己独立的Opcode定义,比如Get请求的响应应该用AccessAckData(3'b010)。 - 你错误地把
slave_d_source赋值为temp_sink,但应该用之前保存的请求方Source ID(temp_source),否则Master无法匹配响应和请求。
(4)状态机逻辑混乱
你的inflight_request变量管理逻辑完全错误,错误地在Put请求时标记了“待响应”,而不是在Get请求时标记。
三、修复后的Slave协议处理代码(Verilog部分)
我重新梳理了状态机,严格遵循Tilelink v1规范:
localparam OP_PUT_FULL = 3'b000; // A通道PutFullData localparam OP_GET = 3'b100; // A通道Get localparam D_OP_ACK_DATA = 3'b010;// D通道带数据响应(对应Get请求) reg [10:0] pending_source; // 保存待响应请求的Source ID reg [2:0] pending_size; // 保存待响应请求的传输大小 reg [1:0] state; localparam IDLE = 2'd0; localparam PROCESSING_GET = 2'd1; reg a_fire; reg d_fire; reg [63:0] accumulator_data; assign a_fire = slave_a_ready && slave_a_valid; assign d_fire = slave_d_ready && slave_d_valid; always @(posedge clock) begin if (reset) begin state <= IDLE; accumulator_data <= 64'b0; slave_a_ready <= 1'b0; slave_d_valid <= 1'b0; pending_source <= 11'd0; pending_size <= 3'd0; end else begin case (state) IDLE: begin slave_a_ready <= 1'b1; // 常态下准备接收A通道请求 if (a_fire) begin case (slave_a_opcode) OP_PUT_FULL: begin // 处理PutFull请求:仅保存并累加数据,无需D响应 accumulator_data <= slave_a_data + 5; $display("[MMIO] Processed PUT_FULL data: 0x%x → 0x%x at time %t", slave_a_data, accumulator_data, $time); end OP_GET: begin // 处理Get请求:需要返回D响应,进入处理状态 $display("[MMIO] Received GET request for addr 0x%x at time %t", slave_a_address, $time); // 保存响应所需的请求参数 pending_source <= slave_a_source[10:0]; pending_size <= slave_a_size; // 暂时关闭A通道接收,避免并发请求 slave_a_ready <= 1'b0; state <= PROCESSING_GET; end default: begin $display("[MMIO] Unsupported opcode: %b", slave_a_opcode); end endcase end end PROCESSING_GET: begin // 组装并发送D通道响应 slave_d_valid <= 1'b1; slave_d_opcode <= D_OP_ACK_DATA; slave_d_param <= 3'b000; slave_d_size <= pending_size; slave_d_source <= pending_source; slave_d_sink <= 0; slave_d_data <= accumulator_data; // 返回累加后的数据 slave_d_denied <= 1'b0; slave_d_corrupt <= 1'b0; if (d_fire) begin // 响应已完成握手,回到IDLE状态 $display("[MMIO] Sent GET response: 0x%x at time %t", accumulator_data, $time); slave_d_valid <= 1'b0; slave_a_ready <= 1'b1; state <= IDLE; end end endcase end end
四、额外的Chisel配置检查
你的Chisel Slave节点配置基本没问题,但可以再确认两点:
AddressSet(0x70000000L, 0x0001ffffL)的地址范围和测试程序的MMIO_BASE_ADDR完全匹配,没问题;supportsGet和supportsPutFull都已正确开启,确保Master的请求能被Slave接收。
五、修复后的预期输出
当你修复完所有问题后,测试程序应该会输出类似这样的结果:
TL MMIO Test Testing Slave Interface: [CORE] Sending data: 0xdead000000000001 [MMIO] Processed PUT_FULL data: 0xdead000000000001 → 0xdead000000000006 at time xxx [CORE] Issuing GET request... [MMIO] Received GET request for addr 0x70000000 at time xxx [MMIO] Sent GET response: 0xdead000000000006 at time xxx [CORE] Received data from slave: 0xdead000000000006
内容来源于stack exchange

