柜台
在此示例中,我们将开发和测试支持Verilog代码生成的计数器模型,并使用云编译器测试其性能。
让我们从分析实现的模型开始。 这是一个使用基本块制作的简单模型。 它支持代码生成,包括用于比较运算符块的代码生成模板。 生成模板本身如下所示。
该代码是一个凿子代码生成器,旨在生成逻辑运算符的块(例如, ==, !=, <, >). 它使用内置的帮助器函数和标签(例如, //!, /*!)来生成Verilog代码。
//! BlockType=:RelationalOperator
//! TargetLang=:凿子
/*! #----------------------------------------------------# */
/*! @定义
函数show_chisel_type(x::typ)::字符串
如果x.bits==1&&x.fractional_bits==0
out="Bool()"
elseif x.fractional_bits==0
out=(x.is_unsigned? "U":"S")*"Int($(x.bits).W)"
else
out = "FixedPoint($(x.\text{位})\text{。}W,$(x.fractional_bits).BP)"
end
out
end
function show_chisel_type(x::signal) :: String
base_type = show_chisel_type(x.ty)
len = x.rows * x.cols
len == 1 ? base_type : "Vec($(\text{伦}), $(base_type))"
end
*/
val $(\text{输出}(1))=\text{电线}($(show_chisel_type(output(1))))
/*! #----------------------------------------------------# */
/*! @Step
function patch_op(op)
op == "==" ? "===" : op == "~=" ? "=/=" : op
end
function get_idx(len, blen)
if len == 1
return ""
elseif len == blen
return "(i)"
else
return "(i % $(len))"
结束
结束
函数maybe_zext(sig,sig2)
如果sig.ty.is_unsigned&&!sig2.ty.is_unsigned
返回"。zext"
结束
""
结束
函数impl(sig1,sig2,op,out)
dim0=dim(出)
dim1=dim(sig1)
dim2=dim(sig2)
blen=lcm(dim1,dim2)#广播长度
如果blen==1
loop_decl=""
其他
loop_decl="for(i<-0直到 $(blen)) "
end
"$\text{(}loop_decl\text{)}$(output(1))$(get_idx(dim0,blen)):= \
$(sig1)$(get_idx(dim1,blen))$(maybe_zext(sig1, sig2)) \
$(patch_op(op)) \
$(sig2)$(get_idx(dim2,blen))$(maybe_zext(sig2, sig1))"
end
*/
$(impl(输入(1),输入(2),param。运算符,输出(1)))
/*! #----------------------------------------------------# */
代码是对块的逐步描述。
1. 函数的定义(块 @Definitions)
show_chisel_type(x::typ)
定义如何表示类型 x 在凿子语法:
Bool()如果是1位布尔值。UInt(...)或SInt(...)如果它是整数类型(没有小数部分)。FixedPoint(...)如果有一个小数部分(定点)。
show_chisel_type(x::signal)
如果信号是数组(Vec),然后返回 Vec(n, base_type) 否则,就 base_type.
2. 输出信号的公告
``'凿子
瓦尔 (show_chisel_type(输出(1))))
创建输出信号为 `Wire(...)`,其类型由上述功能决定。
---
**3. 生成块体(块 `@Step`)**
`patch_op(op)`
将运算符转换为凿子语法:
- `"=="` → `"==="`
- `"~="` → `"=/="` (不平等)
`get_idx(len, blen)`
它用于访问向量(海量)逻辑中的索引。:
-回报 `""` if是一个标量。
- `(i)` 如果长度匹配。
- `(i % len)` 如果需要广播。
`maybe_zext(sig, sig2)`
如果其中一个信号是 — `unsigned` 而另一个 — `signed` 可能需要 `zext` (零扩展到兼容格式)。
`impl(sig1, sig2, op, out)`
字符串生成的主要功能:
-确定信号的大小及其广播长度;
-如果有必要,它包装在 `for`-一个循环。
-生成表单的赋值
``'凿子
输出(i):=input1(i)<op>input2(i)
与处理 zext、运算符的索引和翻译。
该代码实现了一个通用凿块生成器,用于逻辑运算,支持矢量信号、广播和类型转换。 它被编译成凿子代码,用于块的硬件描述,例如,用于FPGA或ASIC。
接下来,让我们看一下实现的模型本身及其生成设置。

正如我们所看到的,该模型非常简单,有三个输入端口。:
- 执行计数器递增的步骤;
- 计数器的最大允许值的限制;
- 重置计数器。
现在我们已经弄清楚了模型,我们将生成它的代码,并根据生成的C函数并基于手写的Verilog测试台验证生成的代码,在添加一个带有 compare.cgt 在途中。
function run_model(name_model)
Path = string(@__DIR__) * "/" * name_model * ".engee"
if name_model in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
model = engee.open( name_model ) # Открыть модель
model_output = engee.run( model, verbose=true ); # Запустить модель
else
model = engee.load( Path, force=true ) # Загрузить модель
model_output = engee.run( model, verbose=true ); # Запустить модель
engee.close( name_model, force=true ); # Закрыть модель
end
return model_output
end
run_model("Counter_C_test")
Cnt_ref = simout["Counter_C_test/Cnt_ref"].value;
Cnt_C = simout["Counter_C_test/Cnt_C"].value;
plot(Cnt_ref)
plot!(Cnt_C)
正如我们所看到的,使用代码和源模型的结果是相同的,这表明代码生成器正常工作。
现在让我们继续编写生成代码的测试绑定。
``'verilog
模块count_Count(
输入时钟,
重置,
输入[31:0]io_Step,
io_Limit_max,
输入io_Reset,
输出[31:0]io_Cnt
);
reg[31:0]UnitDelay_state;
电线[31:0]开关=
$signed(UnitDelay_state) > $签名(io_Limit_max)/io_Reset? 32'0:UnitDelay_state;
总是@(posedge时钟)开始
如果(重置)
UnitDelay_state<=32'0;
其他
UnitDelay_state<=io_Step+开关;
结束//总是@(posedge)
分配io_Cnt=开关;
端模,端模
模块测试台;
reg时钟;
reg重置;
reg[31:0]io_Step;
reg[31:0]io_Limit_max;
注册io_Reset;
线[31:0]io_Cnt;
//实例化被测模块
count_Count dut(
.时钟(clock),
.重置(reset),
.io_Step(io_Step),
.io_Limit_max(io_Limit_max),
.io_Reset(io_Reset),
.io_Cnt(io_Cnt)
);
//时钟生成
总是5时钟=~时钟;
//测试顺序
初始开始
//初始化信号
时钟=0;
重置=1;
io_Step=3;
io_Limit_max=27;
io_Reset=0;
//显示标题
$display("tStep\tLimit\tCnt");
$监视器(io_Step,io_Limit_max,io_Cnt);
//应用重置
10复位=0;
//运行模拟直到计数器结束
200;
$完成;
结束
端模,端模
---
模块 `testbench` 它用于检查计数器的操作. 下面我们列出他执行的操作。
1. 初始化信号:
- `clock` = 0;
- `reset` = 1;
- `io_Step` = 3;
- `io_Limit_max` = 27;
- `io_Reset` = 0.
2. 产生周期为10个时钟周期的时钟信号。
3. 在10个时钟周期后停用信号 `reset`.
4. 输出值 `io_Step`, `io_Limit_max` 和 `io_Cnt` 来监控计数器的行为。
5. 200个时钟周期后完成仿真。
现在让我们使用JDoodle Verilog在线网站运行我们的测试。 它是一个方便的在线平台,可以直接在浏览器中编写、编译和运行Verilog代码。
# display(MIME("text/html"),
# """<iframe
# src="https://www.jdoodle.com/execute-verilog-online"
# width="750" height="500"
# style="border: none;">
# </iframe>""")
结论
仿真结果表明代码工作正常。
并且它与模型中获得的结果相似。 这意味着生成的计数器可用于FPGA操作。
