前言
在RTL设计里,case是一个常用的综合语法,用于根据变量值来选择适当的逻辑电路,语法很简单:
case(Value)
XXXA:
XXXB:
XXXC:
default:
endcase
想象一个场景,在标准的AXI4-Stream接口中,tkeep信号每一个bit表示一个字节的数据是否有效,当我们需要根据tkeep信号来计算这一拍有多少有效数据时这里的代码会是什么样子……
case(tkeep)
64'h1:byteCnt='d1;
64'h3:byteCnt='d2;
……
64'hffffffffffffffff:byteCnt='d64;
default:byteCnt='d64;
endcase
这种代码写的手有点儿累(又没啥技术含量)……
在SpinalHDL里该如何做呢?
switch
SpinalHDL提供了switch方法用于实现Verilog里case语句的实现:
switch(x){
is(value1){
//execute when x === value1
}
is(value2){
//execute when x === value2
}
default{
//execute if none of precedent condition meet
}
}
看起来似乎和Verilog里的差不多,但SpinalHDL基于Scala语言,可借助高级语言进行方便的代码构建:
在SpinalHDL里,我们可以更快速高效的实现tkeep到byteCnt的转换:
val keepIndex=for(index<-1 to 64) yield (BigInt(1)<<index)-1
switch(io.qdma_st_c2h_tkeep){
for(index<-1 to 64){
is(B((BigInt(1)<<index)-1,64 bits)){
io.cnt:=U(index,log2Up(64+1) bits)
}
}
default{
io.cnt:=U(64,log2Up(64+1) bits)
}
}
SpinalHDL在生成RTL时,仅SpinalHDL提供的语法会生成RTL电路,而其他代码则是起指导生成电路的作用,在上面代码里,keepIndex记录了tkeep需要匹配的值,第三行代码则是指定规则,为index从1到64的取值,均生成"is"综合语句,而在真正生成RTL电路时,只有switch、is这些SpinalHDL提供的语句才会生成电路。生成的RTL语句如下:
always @ (*) begin
case(qdma_st_c2h_tkeep)
64'h0000000000000001 : begin
cnt = 7'h01;
end
64'h0000000000000003 : begin
cnt = 7'h02;
end
64'h0000000000000007 : begin
cnt = 7'h03;
end
64'h000000000000000f : begin
cnt = 7'h04;
end
64'h000000000000001f : begin
cnt = 7'h05;
end
64'h000000000000003f : begin
cnt = 7'h06;
end
64'h000000000000007f : begin
cnt = 7'h07;
end
64'h00000000000000ff : begin
cnt = 7'h08;
end
64'h00000000000001ff : begin
cnt = 7'h09;
end
64'h00000000000003ff : begin
cnt = 7'h0a;
end
64'h00000000000007ff : begin
cnt = 7'h0b;
end
64'h0000000000000fff : begin
cnt = 7'h0c;
end
64'h0000000000001fff : begin
cnt = 7'h0d;
end
64'h0000000000003fff : begin
cnt = 7'h0e;
end
64'h0000000000007fff : begin
cnt = 7'h0f;
end
64'h000000000000ffff : begin
cnt = 7'h10;
end
64'h000000000001ffff : begin
cnt = 7'h11;
end
64'h000000000003ffff : begin
cnt = 7'h12;
end
64'h000000000007ffff : begin
cnt = 7'h13;
end
64'h00000000000fffff : begin
cnt = 7'h14;
end
64'h00000000001fffff : begin
cnt = 7'h15;
end
64'h00000000003fffff : begin
cnt = 7'h16;
end
64'h00000000007fffff : begin
cnt = 7'h17;
end
64'h0000000000ffffff : begin
cnt = 7'h18;
end
64'h0000000001ffffff : begin
cnt = 7'h19;
end
64'h0000000003ffffff : begin
cnt = 7'h1a;
end
64'h0000000007ffffff : begin
cnt = 7'h1b;
end
64'h000000000fffffff : begin
cnt = 7'h1c;
end
64'h000000001fffffff : begin
cnt = 7'h1d;
end
64'h000000003fffffff : begin
cnt = 7'h1e;
end
64'h000000007fffffff : begin
cnt = 7'h1f;
end
64'h00000000ffffffff : begin
cnt = 7'h20;
end
64'h00000001ffffffff : begin
cnt = 7'h21;
end
64'h00000003ffffffff : begin
cnt = 7'h22;
end
64'h00000007ffffffff : begin
cnt = 7'h23;
end
64'h0000000fffffffff : begin
cnt = 7'h24;
end
64'h0000001fffffffff : begin
cnt = 7'h25;
end
64'h0000003fffffffff : begin
cnt = 7'h26;
end
64'h0000007fffffffff : begin
cnt = 7'h27;
end
64'h000000ffffffffff : begin
cnt = 7'h28;
end
64'h000001ffffffffff : begin
cnt = 7'h29;
end
64'h000003ffffffffff : begin
cnt = 7'h2a;
end
64'h000007ffffffffff : begin
cnt = 7'h2b;
end
64'h00000fffffffffff : begin
cnt = 7'h2c;
end
64'h00001fffffffffff : begin
cnt = 7'h2d;
end
64'h00003fffffffffff : begin
cnt = 7'h2e;
end
64'h00007fffffffffff : begin
cnt = 7'h2f;
end
64'h0000ffffffffffff : begin
cnt = 7'h30;
end
64'h0001ffffffffffff : begin
cnt = 7'h31;
end
64'h0003ffffffffffff : begin
cnt = 7'h32;
end
64'h0007ffffffffffff : begin
cnt = 7'h33;
end
64'h000fffffffffffff : begin
cnt = 7'h34;
end
64'h001fffffffffffff : begin
cnt = 7'h35;
end
64'h003fffffffffffff : begin
cnt = 7'h36;
end
64'h007fffffffffffff : begin
cnt = 7'h37;
end
64'h00ffffffffffffff : begin
cnt = 7'h38;
end
64'h01ffffffffffffff : begin
cnt = 7'h39;
end
64'h03ffffffffffffff : begin
cnt = 7'h3a;
end
64'h07ffffffffffffff : begin
cnt = 7'h3b;
end
64'h0fffffffffffffff : begin
cnt = 7'h3c;
end
64'h1fffffffffffffff : begin
cnt = 7'h3d;
end
64'h3fffffffffffffff : begin
cnt = 7'h3e;
end
64'h7fffffffffffffff : begin
cnt = 7'h3f;
end
64'hffffffffffffffff : begin
cnt = 7'h40;
end
default : begin
cnt = 7'h40;
end
endcase
end
倘若我不告诉你,焉知我这段代码不是手写的呢(你先手写着,我去刷会儿手机~)?
同样,针对case多个匹配执行相同操作,switch也同样支持,实例如下:
switch(io.dataIn){
is(1,2) {
io.dataOut:=3
}
is(0){
io.dataOut:=2
}
default{
io.dataOut:=0
}
}
对应的RTL同样和手写RTL无二:
always @ (*) begin
case(dataIn)
4'b0001, 4'b0010 : begin
dataOut = 2'b11;
end
4'b0000 : begin
dataOut = 2'b10;
end
default : begin
dataOut = 2'b00;
end
endcase
end
SpinalHDL本质上还是不同于HLS仍需要硬件设计思维,但对于Verilog/SystemVerilog来讲,依旧有鸟枪换炮的优势!
END
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/xE-0sw5DJMn3QVCyFcUYUA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。