在FPGA的开发过程中,在线抓取波形往往是终极调试大法。而如何抓取信号,相信做逻辑开发的小伙伴都是轻车熟路,张口就来,无非两种方式嘛:待跟踪信号添加原语或者手动例化Ila。而在SpinalHDL里,我们的代码是基于Scala来完成的,虽然生成的RTL代码可读,但我好不容易上手了SpinalHDL了你要我添加待跟踪波形信号时去生成的RTL代码里去寻找待添加波形信号么?
图样图森破,在SpinalHDL里,我们一样可以实现优雅的添加待跟踪波形信号。
姿势一
在Verilog代码里,我们想要添加波形跟踪信号时往往在待跟踪信号上添加厂商约束原语,如下所示:
// Marks an internal wire for debug
(* MARK_DEBUG = "TRUE" *) wire debug_wire,
so easy!貌似在SpinalHDL生成的RTL代码添加个原语约束也很容易,但学会了SpinalHDL怎么可以这么做呢,在SpinalHDL里,我们可以同样实现。
SpinalHDL提供了addAttribute语法来帮助我们实现给某个信号添加我们想要的综合实现属性。
诸君请看:
class Counter(width :Int) extends Component {
val io=new Bundle{
val clear =in Bool()
val value=out UInt(width bits)
val full =out Bool()
}
noIoPrefix()
/*
累加器实现
*/
val cnt=new Area {
val counter=Reg(UInt(width bits)) init(0)
counter.addAttribute("MARK_DEBUG","TRUE")
when(io.clear){
counter:=0
} otherwise{
counter:=counter+1
}
}.setName("")
io.value:=cnt.counter
io.full:=cnt.counter.andR
}
在这段简单的代码里,我们想要抓取跟踪counter,只需要在代码里给counter添加addAttribute属性即可,来看下他生成的RTL代码:
module Counter (
input clear,
output [7:0] value,
output full,
input clk,
input reset
);
(* MARK_DEBUG = "TRUE" *) reg [7:0] counter_1_;
assign value = counter_1_;
assign full = (counter_1_ == 8'hff);
always @ (posedge clk) begin
if(reset) begin
counter_1_ <= 8'h0;
end else begin
if(clear)begin
counter_1_ <= 8'h0;
end else begin
counter_1_ <= (counter_1_ + 8'h01);
end
end
end
endmodule
完全实现了我们想要的结果。
姿势二
通过对变量添加addAttribute属性虽然可以用来进行信号波形跟踪,但在PR工程的虚拟JTAG(通过PCIe模拟JTAG环境)场景里这种方式就无能为力了,只能通过例化ILA的方式去添加待波形跟踪信号。在SpinalHDL里,我们可以借助blackBox及scala语言本身实现在SpinalHDL环境里实现对ILA的集成。
class Ila(probeSigWidth:Seq[Int]) extends BlackBox {
val io=new Bundle{
val clk=in Bool()
val probe= in Vec(for(index<- 0 until probeSigWidth.size) yield (UInt((probeSigWidth(index) bits))) setName(s"probe$index"))
}
this.setDefinitionName(s"ila${Ila.ilaIndex}")
noIoPrefix()
mapClockDomain(clock = io.clk)
}
object Ila{
private var ilaIndex=0
private def genTclScript(probeSigWidth:Seq[Int],sampleDepth:Int=1024)={
import java.io._
val createIlaCmd=s"set ilaExit [lsearch -exact [get_ips ila*] ila${Ila.ilaIndex}]\n" +
s"if { $$ilaExit <0} {\n" +
s"create_ip -name ila -vendor xilinx.com -library ip -version 6.2 -module_name ila${ilaIndex}\n" +
s"}\n"
val tclHeader=new PrintWriter(new File(s"generateIla$ilaIndex.tcl"))
tclHeader.write(createIlaCmd)
tclHeader.write(s"set_property CONFIG.C_NUM_OF_PROBES ${probeSigWidth.size} [get_ips ila${ilaIndex}]\n")
tclHeader.write(s"set_property CONFIG.C_EN_STRG_QUAL {1} [get_ips ila${ilaIndex}]\n")
tclHeader.write(s"set_property CONFIG.C_ADV_TRIGGER {true} [get_ips ila${ilaIndex}]\n")
tclHeader.write(s"set_property CONFIG.C_DATA_DEPTH $sampleDepth [get_ips ila${ilaIndex}]\n")
for(probeIndex<- 0 until probeSigWidth.size){
tclHeader.write(s"set_property CONFIG.C_PROBE${probeIndex}_WIDTH {${probeSigWidth(probeIndex)}} [get_ips ila${ilaIndex}]\n")
}
tclHeader.close()
}
上面的代码实现了用于添加ILa单元实现波形追踪的代码,在调用时会同时生成用于生成相应ILa IP的tcl脚本,从而能够轻松的在SpinalHDL中实现对待跟踪波形信号的抓取实现。
下面一段代码展示了如何在SpinalHDL里添加ILA:
case class add(dataWidth:Int) extends Component{
val io=new Bundle{
val data1 =in UInt(dataWidth bits)
val data2=in UInt(dataWidth bits)
val sum=out UInt(dataWidth bits) setAsReg() init(0)
}
noIoPrefix()
val both=io.data1+1
io.sum:=io.data1+io.data2+both
}
class Sum() extends Component{
val io=new Bundle{
val data1=in UInt(8 bits)
val data2=in UInt(8 bits)
val sum=out UInt(8 bits)
}
noIoPrefix()
val sum=new add(8)
io.data1<>sum.io.data1
io.data2<>sum.io.data2
io.sum<>sum.io.sum
val ila_probe=Ila(Seq(sum.both.pull()),1024)
val ila_probe1=Ila(Seq(io.data1,io.data2,io.sum),2048)
}
在上述代码中。就像声明一个类对象一样调用ILa,同时借助SpinalHDL对跨层次信号调用的支持(A.B.xxx.pull(),想想在RTL里如果通过例化ILA的方式添加跨层次信号我们要多做多少工作~),我们可以在顶层直接把所有待跟踪信号添加到一个ILA中,我们看下生成的RTL代码及Tcl脚本:
module Sum (
input [7:0] data1,
input [7:0] data2,
output [7:0] sum_2,
input clk,
input reset
);
wire [7:0] sum_1_sum_1;
wire [7:0] sum_1__zz_1;
add sum_1 (
.data1 (data1[7:0] ), //i
.data2 (data2[7:0] ), //i
.sum_1 (sum_1_sum_1[7:0] ), //o
._zz_1 (sum_1__zz_1[7:0] ), //o
.clk (clk ), //i
.reset (reset ) //i
);
ila0 ila_probe (
.clk (clk ), //i
.probe0 (sum_1__zz_1[7:0] ) //i
);
ila1 ila_probe1 (
.clk (clk ), //i
.probe0 (data1[7:0] ), //i
.probe1 (data2[7:0] ), //i
.probe2 (sum_2[7:0] ) //i
);
assign sum_2 = sum_1_sum_1;
endmodule
module add (
input [7:0] data1,
input [7:0] data2,
output reg [7:0] sum_1,
output [7:0] _zz_1,
input clk,
input reset
);
wire [7:0] _zz_2;
wire [7:0] both;
assign _zz_2 = (data1 + data2);
assign both = (data1 + 8'h01);
assign _zz_1 = both;
always @ (posedge clk or posedge reset) begin
if (reset) begin
sum_1 <= 8'h0;
end else begin
sum_1 <= (_zz_2 + both);
end
end
endmodule
set ilaExit [lsearch -exact [get_ips ila*] ila0]
if { $ilaExit <0} {
create_ip -name ila -vendor xilinx.com -library ip -version 6.2 -module_name ila0
}
set_property CONFIG.C_NUM_OF_PROBES 1 [get_ips ila0]
set_property CONFIG.C_EN_STRG_QUAL {1} [get_ips ila0]
set_property CONFIG.C_ADV_TRIGGER {true} [get_ips ila0]
set_property CONFIG.C_DATA_DEPTH 1024 [get_ips ila0]
set_property CONFIG.C_PROBE0_WIDTH {8} [get_ips ila0]
set ilaExit [lsearch -exact [get_ips ila*] ila1]
if { $ilaExit <0} {
create_ip -name ila -vendor xilinx.com -library ip -version 6.2 -module_name ila1
}
set_property CONFIG.C_NUM_OF_PROBES 3 [get_ips ila1]
set_property CONFIG.C_EN_STRG_QUAL {1} [get_ips ila1]
set_property CONFIG.C_ADV_TRIGGER {true} [get_ips ila1]
set_property CONFIG.C_DATA_DEPTH 2048 [get_ips ila1]
set_property CONFIG.C_PROBE0_WIDTH {8} [get_ips ila1]
set_property CONFIG.C_PROBE1_WIDTH {8} [get_ips ila1]
set_property CONFIG.C_PROBE2_WIDTH {8} [get_ips ila1]
把生成的RTL代码和tcl脚本放进Vivado里直接跑连手动例化ILa IP的功夫都省了~
END
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/zLwkF\_UJbO0u-YKcEAu4wA
微信公众号:
推荐阅读
- 从Verilog到SpinalHDL
更多SpinalHDL技术干货请关注Spinal FPGA专栏。