You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Verilog 8x4单端口BRAM推断与排序实现报错求助

问题排查与修正方案

一、先解决编译核心错误:timescale解析问题

报错Error: Parsing info not available during refresh指向timescale,通常是以下原因:

  • 指令位置错误:timescale必须放在所有模块定义之前,不能插在模块中间。
  • 语法格式错误:严格遵循timescale <时间单位>/<时间精度>格式,例如timescale 1ns/1ps,不能有多余空格或拼写错误。
  • 多文件冲突:如果工程包含多个Verilog文件,确保所有文件的timescale指令一致,避免工具解析混乱。

二、单端口BRAM推断规范

8x4的单端口BRAM完全符合FPGA工具的推断条件,写法需严格遵循时序逻辑规则:

  1. 端口与内部逻辑规范
    单端口BRAM的读数据必须由时钟沿触发的reg存储,不要随意改为wire(wire无法寄存时序输出)。标准推断模板如下:
    module DS #(
        parameter WIDTH = 4,
        parameter DEPTH = 8
    )(
        input clk,
        input we,
        input [$clog2(DEPTH)-1:0] addr,
        input [WIDTH-1:0] din,
        output reg [WIDTH-1:0] dout
    );
        reg [WIDTH-1:0] ram [DEPTH-1:0];
        
        always @(posedge clk) begin
            if(we) begin
                ram[addr] <= din;
            end
            dout <= ram[addr]; // 单端口BRAM时钟沿输出读数据,读随写逻辑
        end
    endmodule
    
  2. 避免推断为分布式RAM
    禁止在组合逻辑中直接读取ram数组,所有读写操作必须放在时钟沿触发的always块内,确保工具识别为块RAM资源。

三、无for循环的排序模块实现

采用并行比较交换的奇偶排序网络实现8个4位数据排序,配合状态机控制BRAM的读写流程:

  1. 状态机划分
    必须分阶段控制,避免读写冲突:IDLE(初始化)→READ_RAM(读取所有数据到寄存器组)→SORTING(多轮并行比较交换)→WRITE_RAM(写回排序后数据)→DONE(完成)。
    核心排序逻辑示例(展开冒泡排序的循环,无for循环):
    module Sort_mem #(
        parameter WIDTH = 4,
        parameter DEPTH = 8
    )(
        input clk,
        input rst_n,
        output reg we,
        output reg [$clog2(DEPTH)-1:0] addr,
        output reg [WIDTH-1:0] din,
        input [WIDTH-1:0] dout
    );
        reg [2:0] state;
        reg [WIDTH-1:0] data_reg [DEPTH-1:0];
        reg [2:0] cnt;
        
        localparam IDLE = 3'b000;
        localparam READ_RAM = 3'b001;
        localparam SORT_ODD = 3'b010;
        localparam SORT_EVEN = 3'b011;
        localparam WRITE_RAM = 3'b100;
        localparam DONE = 3'b101;
        
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                state <= IDLE;
                we <= 1'b0;
                addr <= 3'd0;
                cnt <= 3'd0;
            end else begin
                case(state)
                    IDLE: state <= READ_RAM;
                    READ_RAM: begin
                        data_reg[cnt] <= dout;
                        cnt <= cnt + 1'b1;
                        addr <= addr + 1'b1;
                        if(cnt == 3'd7) begin
                            state <= SORT_ODD;
                            cnt <= 3'd0;
                        end
                    end
                    SORT_ODD: begin
                        // 奇数位比较交换
                        if(data_reg[0] > data_reg[1]) {data_reg[0], data_reg[1]} <= {data_reg[1], data_reg[0]};
                        if(data_reg[2] > data_reg[3]) {data_reg[2], data_reg[3]} <= {data_reg[3], data_reg[2]};
                        if(data_reg[4] > data_reg[5]) {data_reg[4], data_reg[5]} <= {data_reg[5], data_reg[4]};
                        if(data_reg[6] > data_reg[7]) {data_reg[6], data_reg[7]} <= {data_reg[7], data_reg[6]};
                        state <= SORT_EVEN;
                    end
                    SORT_EVEN: begin
                        // 偶数位比较交换
                        if(data_reg[1] > data_reg[2]) {data_reg[1], data_reg[2]} <= {data_reg[2], data_reg[1]};
                        if(data_reg[3] > data_reg[4]) {data_reg[3], data_reg[4]} <= {data_reg[4], data_reg[3]};
                        if(data_reg[5] > data_reg[6]) {data_reg[5], data_reg[6]} <= {data_reg[6], data_reg[5]};
                        cnt <= cnt + 1'b1;
                        // 8个元素需3轮奇偶排序完成
                        if(cnt == 3'd3) begin
                            state <= WRITE_RAM;
                            cnt <= 3'd0;
                            addr <= 3'd0;
                            we <= 1'b1;
                        end else state <= SORT_ODD;
                    end
                    WRITE_RAM: begin
                        din <= data_reg[cnt];
                        addr <= cnt;
                        cnt <= cnt + 1'b1;
                        if(cnt == 3'd7) begin
                            we <= 1'b0;
                            state <= DONE;
                        end
                    end
                    DONE: ; // 保持完成状态
                endcase
            end
        end
    endmodule
    
  2. 读写冲突规避
    单端口BRAM同一时钟周期只能执行读或写操作,状态机中READ_RAM阶段禁止置位we,WRITE_RAM阶段仅执行写操作。

四、测试平台修正

仿真无界面和结果通常是测试平台的问题,需完善以下几点:

  1. 时钟与复位生成
    initial begin
        clk = 1'b0;
        forever #5 clk = ~clk; // 100MHz时钟
    end
    
    initial begin
        rst_n = 1'b0;
        #20 rst_n = 1'b1; // 复位20ns后释放
    end
    
  2. BRAM初始化与排序触发
    先写入测试数据,再启动排序流程:
    initial begin
        we = 1'b0;
        addr = 3'd0;
        din = 4'd0;
        #20;
        // 写入测试数据
        we = 1'b1;
        addr = 3'd0; din = 4'd7; #10;
        addr = 3'd1; din = 4'd2; #10;
        addr = 3'd2; din = 4'd5; #10;
        addr = 3'd3; din = 4'd1; #10;
        addr = 3'd4; din = 4'd8; #10;
        addr = 3'd5; din = 4'd3; #10;
        addr = 3'd6; din = 4'd6; #10;
        addr = 3'd7; din = 4'd4; #10;
        we = 1'b0;
        #10; // 等待数据稳定后启动排序
    end
    
  3. 波形文件生成
    添加波形转储指令,确保仿真工具能捕获信号:
    initial begin
        $dumpfile("wave.vcd");
        $dumpvars(0, testbench); // 0表示转储所有层级信号
    end
    
    部分工具(如ModelSim)需手动将模块端口、内部信号拖入波形窗口。

五、其他排查点

  • 检查模块端口连接是否正确:Sort_mem的dout需连接DS的dout,we、addr、din需对应连接DS的同名端口。
  • 确认参数一致性:所有模块的WIDTH(4)、DEPTH(8)参数需保持一致,避免位宽不匹配。
  • 查看完整编译日志:除timescale错误外,可能存在隐藏语法问题(如缺分号、变量未定义)。

内容的提问来源于stack exchange,提问作者Kağan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:53:10