Merge pull request 'Merge dev to main.' (#1) from dev into main
Reviewed-on: https://git.xb/xinb/ic_com/pulls/1
This commit is contained in:
commit
dc78137ed9
|
@ -1,3 +1,8 @@
|
|||
# ic_com
|
||||
|
||||
数字芯片设计公共库
|
||||
|
||||
## 目录结构
|
||||
- `lib/`:核心功能模块(如编码器、FIFO等)
|
||||
- `tb/`:各模块的仿真测试平台
|
||||
- `doc/`:文档与使用说明
|
|
@ -0,0 +1,57 @@
|
|||
# ic_com 库功能简介
|
||||
|
||||
## 目录结构
|
||||
- `lib/`:核心功能模块(如编码器、FIFO等)
|
||||
- `tb/`:各模块的仿真测试平台
|
||||
- `doc/`:文档与使用说明
|
||||
|
||||
## 主要功能模块
|
||||
### 1. 编码/解码
|
||||
- `enc_dec/oh_bin/oh2bin.sv`:参数化 one-hot 到二进制编码器,支持面积/频率优化,适用于高性能和低功耗场景。
|
||||
|
||||
### 2. FIFO
|
||||
- `fifo/sync/fifo_sync.sv`:同步FIFO,支持参数化数据宽度和深度,适合多场景缓存。
|
||||
|
||||
## 仿真与验证
|
||||
- `tb/enc_dec/oh_bin/oh2bin_tb.sv`:oh2bin编码器的时序仿真测试,支持自动化验证。
|
||||
- `tb/fifo/sync/fifo_sync_tb.sv`:同步FIFO的功能仿真测试。
|
||||
|
||||
## 波形与调试
|
||||
- 推荐使用VCS仿真,支持FSDB波形输出,便于使用Verdi/KDB调试。
|
||||
|
||||
## 快速仿真命令
|
||||
见下方示例。
|
||||
|
||||
---
|
||||
|
||||
# VCS仿真命令示例
|
||||
|
||||
## 1. 生成.f文件(文件列表)
|
||||
|
||||
- 默认各模块`.f`文件需要配置环境变量`IC_COM_DIR`为仓库根目录。
|
||||
|
||||
- `lib.f`:包含所有lib目录下的源文件
|
||||
- `tb.f`:包含所有tb目录下的测试平台文件
|
||||
|
||||
## 2. 推荐VCS命令(含FSDB波形与KDB调试)
|
||||
```sh
|
||||
vcs -full64 -sverilog -debug_access+all +v2k +vcs+lic+wait \
|
||||
-f lib.f -f tb.f \
|
||||
-l vcs.log \
|
||||
-kdb \
|
||||
+fsdbfile+${USER}_sim.fsdb \
|
||||
+fsdb+autoflush \
|
||||
+vcsd \
|
||||
+vcs+dumpvars+all \
|
||||
+vcs+fsdbon \
|
||||
+notimingcheck \
|
||||
+define+FSDB \
|
||||
+define+KDB
|
||||
```
|
||||
- 默认fsdb文件名为`${USER}_sim.fsdb`,可自动区分不同用户仿真结果。
|
||||
- 支持KDB调试,推荐配合Verdi使用。
|
||||
|
||||
## 3. 运行仿真
|
||||
```sh
|
||||
./simv
|
||||
```
|
|
@ -0,0 +1,2 @@
|
|||
$IC_COM_DIR/lib/enc_dec/oh_bin/oh2bin.sv
|
||||
$IC_COM_DIR/tb/enc_dec/oh_bin/oh2bin_tb.sv
|
|
@ -0,0 +1,2 @@
|
|||
$IC_COM_DIR/lib/fifo/sync/fifo_sync.sv
|
||||
$IC_COM_DIR/tb/fifo/sync/fifo_sync_tb.sv
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
// doc/lib.f: ic_com库所有核心源文件,引用各子库f文件
|
||||
lib/enc_dec/oh_bin/oh_bin.f
|
||||
lib/fifo/sync/sync.f
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
// doc/tb.f: ic_com库所有测试平台文件,引用各子库f文件
|
||||
lib/enc_dec/oh_bin/oh_bin.f
|
||||
lib/fifo/sync/sync.f
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
// =============================================================
|
||||
// 模块名称:oh2bin
|
||||
// 功能描述:参数化 one-hot 到二进制编码器
|
||||
// 参数:
|
||||
// WIDTH - one-hot 输入宽度
|
||||
// AREA_OPT- 1: 面积优化,0: 频率优化
|
||||
// =============================================================
|
||||
module oh2bin #(
|
||||
parameter WIDTH = 8, // one-hot 输入宽度
|
||||
parameter AREA_OPT = 1 // 1: 面积优化,0: 频率优化
|
||||
)(
|
||||
input logic [WIDTH-1:0] oh, // one-hot 输入
|
||||
output logic [$clog2(WIDTH)-1:0] bin // 二进制输出
|
||||
);
|
||||
|
||||
// =========================================================
|
||||
// 面积优化方案:使用for循环,优先节省面积
|
||||
// =========================================================
|
||||
generate
|
||||
if (AREA_OPT) begin : gen_area_opt
|
||||
always_comb begin
|
||||
bin = '0;
|
||||
for (int i = 0; i < WIDTH; i++) begin
|
||||
if (oh[i]) bin = i[$clog2(WIDTH)-1:0];
|
||||
end
|
||||
end
|
||||
end else begin : gen_speed_opt
|
||||
// =========================================================
|
||||
// 频率优化方案:并行掩码法,参考pulp-platform/common_cells实现
|
||||
// =========================================================
|
||||
for (genvar j = 0; j < $clog2(WIDTH); j++) begin : gen_jl
|
||||
logic [WIDTH-1:0] tmp_mask; //xinb:bin每1bit的掩码,实际上取巧了。相当于是逆向思维。还可以正向编码,待补充.
|
||||
for (genvar i = 0; i < WIDTH; i++) begin : gen_il
|
||||
logic [$clog2(WIDTH)-1:0] tmp_i;
|
||||
assign tmp_i = i[$clog2(WIDTH)-1:0];
|
||||
assign tmp_mask[i] = tmp_i[j];
|
||||
end
|
||||
assign bin[j] = |(tmp_mask & oh);
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,92 @@
|
|||
|
||||
// 同步FIFO模块,可参数化配置
|
||||
module fifo_sync #(
|
||||
parameter DATA_WIDTH = 8, // 数据位宽
|
||||
parameter DEPTH = 16, // FIFO深度
|
||||
parameter USE_RAM = 0, // 是否使用RAM存储(0: 寄存器数组, 1: RAM)
|
||||
parameter OUT_REG = 1 // 输出是否为reg型(1: 输出寄存器, 0: 直通)
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
input wire wr_en,
|
||||
input wire [DATA_WIDTH-1:0] din,
|
||||
input wire rd_en,
|
||||
output wire [DATA_WIDTH-1:0] dout,
|
||||
output wire empty,
|
||||
output wire full,
|
||||
output wire [$clog2(DEPTH):0] fill_level,
|
||||
output reg err
|
||||
);
|
||||
|
||||
localparam ADDR_WIDTH = $clog2(DEPTH);
|
||||
|
||||
reg [ADDR_WIDTH:0] wr_ptr, rd_ptr;
|
||||
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
|
||||
reg [DATA_WIDTH-1:0] dout_reg;
|
||||
wire [DATA_WIDTH-1:0] dout_wire;
|
||||
|
||||
// 写操作
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
wr_ptr <= 0;
|
||||
end else if (wr_en && !full) begin
|
||||
wr_ptr <= wr_ptr + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// 读操作
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
rd_ptr <= 0;
|
||||
err <= 1'b0;
|
||||
end else begin
|
||||
if (rd_en && empty) begin
|
||||
err <= 1'b1;
|
||||
end else begin
|
||||
err <= 1'b0;
|
||||
end
|
||||
if (rd_en && !empty) begin
|
||||
rd_ptr <= rd_ptr + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// 数据计数
|
||||
assign fill_level = wr_ptr - rd_ptr;
|
||||
assign empty = (wr_ptr == rd_ptr);
|
||||
assign full = (fill_level == DEPTH);
|
||||
|
||||
generate
|
||||
if (USE_RAM) begin : USE_RAM_BLOCK
|
||||
// RAM实现(可替换为实际RAM IP)
|
||||
always @(posedge clk) begin
|
||||
if (wr_en && !full)
|
||||
mem[wr_ptr[ADDR_WIDTH-1:0]] <= din;
|
||||
end
|
||||
assign dout_wire = mem[rd_ptr[ADDR_WIDTH-1:0]];
|
||||
end else begin : USE_REG_BLOCK
|
||||
// 寄存器数组实现
|
||||
always @(posedge clk) begin
|
||||
if (wr_en && !full)
|
||||
mem[wr_ptr[ADDR_WIDTH-1:0]] <= din;
|
||||
end
|
||||
assign dout_wire = mem[rd_ptr[ADDR_WIDTH-1:0]];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// 输出寄存器可选
|
||||
generate
|
||||
if (OUT_REG) begin : OUT_REG_BLOCK
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n)
|
||||
dout_reg <= {DATA_WIDTH{1'b0}};
|
||||
else if (rd_en && !empty)
|
||||
dout_reg <= dout_wire;
|
||||
end
|
||||
assign dout = dout_reg;
|
||||
end else begin : OUT_WIRE_BLOCK
|
||||
assign dout = dout_wire;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,78 @@
|
|||
// =============================================================
|
||||
// 测试模块:oh2bin_tb
|
||||
// 功能描述:onehot到bin编码器仿真测试,默认频率优化方案
|
||||
// 仿真时钟频率:1GHz,周期1ns
|
||||
// =============================================================
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module oh2bin_tb;
|
||||
parameter WIDTH = 8;
|
||||
parameter BINW = $clog2(WIDTH);
|
||||
|
||||
logic clk;
|
||||
logic rst_n;
|
||||
logic [WIDTH-1:0] oh;
|
||||
logic [BINW-1:0] bin;
|
||||
logic [BINW-1:0] bin_q;
|
||||
|
||||
// 实例化被测模块,AREA_OPT=0为频率优化方案
|
||||
oh2bin #(
|
||||
.WIDTH(WIDTH),
|
||||
.AREA_OPT(0)
|
||||
) dut (
|
||||
.oh(oh),
|
||||
.bin(bin)
|
||||
);
|
||||
|
||||
// 时钟生成
|
||||
// 仿真时钟频率:1GHz,周期1ns
|
||||
initial clk = 0;
|
||||
always #0.5 clk = ~clk; // 1ns周期,1GHz
|
||||
|
||||
// 采样输出
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n)
|
||||
bin_q <= '0;
|
||||
else
|
||||
bin_q <= bin;
|
||||
end
|
||||
|
||||
// 参考模型
|
||||
function [BINW-1:0] ref_oh2bin(input [WIDTH-1:0] oh_in);
|
||||
integer i;
|
||||
ref_oh2bin = '0;
|
||||
for (i = 0; i < WIDTH; i = i + 1) begin
|
||||
if (oh_in[i]) ref_oh2bin = i[BINW-1:0];
|
||||
end
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
rst_n = 0;
|
||||
oh = '0;
|
||||
#20;
|
||||
rst_n = 1;
|
||||
$display("==== oh2bin 频率优化方案时序仿真 ====");
|
||||
for (int i = 0; i < WIDTH; i++) begin
|
||||
@(negedge clk);
|
||||
oh = '0;
|
||||
oh[i] = 1'b1;
|
||||
@(negedge clk);
|
||||
if (bin_q !== ref_oh2bin(oh)) begin
|
||||
$display("[ERROR] oh=%b, bin=%0d, ref=%0d", oh, bin_q, ref_oh2bin(oh));
|
||||
end else begin
|
||||
$display("[PASS] oh=%b, bin=%0d", oh, bin_q);
|
||||
end
|
||||
end
|
||||
// 非法输入测试
|
||||
@(negedge clk);
|
||||
oh = '0;
|
||||
@(negedge clk);
|
||||
$display("[INFO] all zero input, bin=%0d", bin_q);
|
||||
@(negedge clk);
|
||||
oh = '1;
|
||||
@(negedge clk);
|
||||
$display("[INFO] all one input, bin=%0d", bin_q);
|
||||
$display("==== 测试结束 ====");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,126 @@
|
|||
|
||||
// =============================================================
|
||||
// 测试模块:fifo_sync_tb
|
||||
// 功能描述:同步FIFO仿真测试
|
||||
// 仿真时钟频率:1GHz,周期0.5ns
|
||||
// =============================================================
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module fifo_sync_tb;
|
||||
parameter DATA_WIDTH = 8;
|
||||
parameter DEPTH = 16;
|
||||
parameter USE_RAM = 0;
|
||||
parameter OUT_REG = 1;
|
||||
|
||||
reg clk;
|
||||
reg rst_n;
|
||||
reg wr_en;
|
||||
reg [DATA_WIDTH-1:0] din;
|
||||
reg rd_en;
|
||||
wire [DATA_WIDTH-1:0] dout;
|
||||
wire empty;
|
||||
wire full;
|
||||
wire [$clog2(DEPTH):0] fill_level;
|
||||
wire err;
|
||||
|
||||
// 实例化DUT
|
||||
fifo_sync #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.DEPTH(DEPTH),
|
||||
.USE_RAM(USE_RAM),
|
||||
.OUT_REG(OUT_REG)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.wr_en(wr_en),
|
||||
.din(din),
|
||||
.rd_en(rd_en),
|
||||
.dout(dout),
|
||||
.empty(empty),
|
||||
.full(full),
|
||||
.fill_level(fill_level),
|
||||
.err(err)
|
||||
);
|
||||
|
||||
// 仿真时钟频率:1GHz,周期0.5ns
|
||||
// 时钟生成
|
||||
initial clk = 0;
|
||||
always #0.25 clk = ~clk; // 0.5ns周期,1GHz
|
||||
|
||||
// 复位
|
||||
initial begin
|
||||
rst_n = 0;
|
||||
wr_en = 0;
|
||||
rd_en = 0;
|
||||
din = 0;
|
||||
#20;
|
||||
rst_n = 1;
|
||||
end
|
||||
|
||||
// 主测试流程
|
||||
initial begin
|
||||
wait(rst_n == 1);
|
||||
@(negedge clk);
|
||||
|
||||
// 写入DEPTH个数据
|
||||
for (int i = 0; i < DEPTH; i++) begin
|
||||
@(negedge clk);
|
||||
wr_en = 1;
|
||||
din = i;
|
||||
rd_en = 0;
|
||||
end
|
||||
@(negedge clk);
|
||||
wr_en = 0;
|
||||
|
||||
// 检查full信号
|
||||
if (!full) $display("[TB][ERROR] FIFO未满!");
|
||||
else $display("[TB][INFO] FIFO已满!");
|
||||
|
||||
// 读出DEPTH个数据
|
||||
for (int i = 0; i < DEPTH; i++) begin
|
||||
@(negedge clk);
|
||||
wr_en = 0;
|
||||
rd_en = 1;
|
||||
end
|
||||
@(negedge clk);
|
||||
rd_en = 0;
|
||||
|
||||
// 检查empty信号
|
||||
if (!empty) $display("[TB][ERROR] FIFO未空!");
|
||||
else $display("[TB][INFO] FIFO已空!");
|
||||
|
||||
// 尝试在empty时读,触发err
|
||||
@(negedge clk);
|
||||
rd_en = 1;
|
||||
@(negedge clk);
|
||||
rd_en = 0;
|
||||
if (err) $display("[TB][INFO] 读空FIFO触发err信号");
|
||||
else $display("[TB][ERROR] 读空FIFO未触发err!");
|
||||
|
||||
// 交错写读
|
||||
for (int i = 0; i < 8; i++) begin
|
||||
@(negedge clk);
|
||||
wr_en = 1;
|
||||
din = i + 100;
|
||||
rd_en = 1;
|
||||
end
|
||||
@(negedge clk);
|
||||
wr_en = 0;
|
||||
rd_en = 0;
|
||||
|
||||
#20;
|
||||
$display("[TB][INFO] 测试结束");
|
||||
$finish;
|
||||
end
|
||||
|
||||
// 监控输出
|
||||
always @(posedge clk) begin
|
||||
if (wr_en && !full)
|
||||
$display("[TB][WRITE] @%0t: din=%0d, wr_ptr=%0d, fill_level=%0d", $time, din, dut.wr_ptr, fill_level);
|
||||
if (rd_en && !empty)
|
||||
$display("[TB][READ ] @%0t: dout=%0d, rd_ptr=%0d, fill_level=%0d", $time, dout, dut.rd_ptr, fill_level);
|
||||
if (err)
|
||||
$display("[TB][ERR ] @%0t: 读空FIFO!", $time);
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue