在我们编写Verilog或者SysytemVerilog时,我们的代码基本都以module来进行组织,而针对一些比较通用的模块组件,我们或组织成一个单独的model,或者放在一个function中(仅限于纯组合逻辑)。本篇介绍SpinalHDL中的Area的概念。
Area
在SpinalHDKL里,与Verilog、SystemVerilog中相对应的概念是Component,当我们的类继承了Component后,与之相应的我们就要定义端口及参数。以我们上篇所提到的例子,我们在例化一个加法器模块时从初阶、到中阶再到高阶,我们仍避免不了连线的话题,只不过是在伴生对象中做了一次封装从而一劳永逸。
为了解决过于细致的模块划分(不断的端口连线)及兼顾代码尽可能复用的准则,SpinalHDL里设计了Area的概念。通过类扩展集成Area,可以有效的避免上述问题。介绍Area之前,先介绍一个概念:
在Scala中,参数的传递均为引用类型,而我们定义的电路对象本身也是一个scala类的实例化对象,作为类的参数传递在类中是可以直接定义电路对象的动作的。
有了Area概念的引入,《SpinalHDL—像软件调用方法般例化模块》中所用到的加法器我们可以这么来定义:
case class addArea(port:sumPort)extends Area{
port.sum.payload:=RegNextWhen(port.dataIn.data1+port.dataIn.data2,port.dataIn.valid)
port.sum.valid:=RegNext(port.dataIn.valid,False)
}
这里我们定义了一个addArea类,其继承了Area,电路对象sumPort作为类的一个参数来传递,在类的实现里,我们实现了对加法器的实现。这种方式我们无需定义伴生对象(object)便可以方便的实现加法器单元的例化:
class addInst2(dataWidth:Int) extends Component{
val io=new Bundle{
val sumport0=slave(sumPort(dataWidth))
val sumport1=slave(sumPort(dataWidth))
}
addArea(io.sumport0)
addArea(io.sumport1)
}
而生成的SystemVerilog代码如下所示:
module addInst2 (
input io_sumport0_dataIn_valid,
input [7:0] io_sumport0_dataIn_payload_data1,
input [7:0] io_sumport0_dataIn_payload_data2,
output io_sumport0_sum_valid,
output [7:0] io_sumport0_sum_payload,
input io_sumport1_dataIn_valid,
input [7:0] io_sumport1_dataIn_payload_data1,
input [7:0] io_sumport1_dataIn_payload_data2,
output io_sumport1_sum_valid,
output [7:0] io_sumport1_sum_payload,
input clk,
input reset
);
reg [7:0] _zz_1;
reg io_sumport0_dataIn_valid_regNext;
reg [7:0] _zz_2;
reg io_sumport1_dataIn_valid_regNext;
assign io_sumport0_sum_payload = _zz_1;
assign io_sumport0_sum_valid = io_sumport0_dataIn_valid_regNext;
assign io_sumport1_sum_payload = _zz_2;
assign io_sumport1_sum_valid = io_sumport1_dataIn_valid_regNext;
always @ (posedge clk) begin
if(io_sumport0_dataIn_valid)begin
_zz_1 <= (io_sumport0_dataIn_payload_data1 + io_sumport0_dataIn_payload_data2);
end
if(io_sumport1_dataIn_valid)begin
_zz_2 <= (io_sumport1_dataIn_payload_data1 + io_sumport1_dataIn_payload_data2);
end
end
always @ (posedge clk or posedge reset) begin
if (reset) begin
io_sumport0_dataIn_valid_regNext <= 1'b0;
io_sumport1_dataIn_valid_regNext <= 1'b0;
end else begin
io_sumport0_dataIn_valid_regNext <= io_sumport0_dataIn_valid;
io_sumport1_dataIn_valid_regNext <= io_sumport1_dataIn_valid;
end
end
endmodule
可以看到,继承了Area的类在例化时对应的RTL代码也是没有对应的module分层的。
Area“副作用”
Area除了可以用于将通用代码从Component中抽取出来的用处之外其另外一个特性便是我们可以在我们的Component代码实现里将我们的代码按模块放置在Area里进行按功能分类,而在生成Verilog时我们定义在Area中的变量也会加上Area的前缀,如此我们可以在仿真或上板抓线时方便的进行信号提取。如下代码:
class areaDeom extends Component{
val io = new Bundle{
val clear = in Bool
val value = out UInt(8 bits)
}
val cntArea=new Area{
val register = Reg(UInt(8 bits)) init(0)
register := register + 1
when(io.clear){
register := 0
}
io.value := register
}
}
其生成的RTL代码如下所示:
module areaDeom (module areaDeom (
input io_clear,
output [7:0] io_value,
input clk,
input reset
);
reg [7:0] cntArea_register;
assign io_value = cntArea_register;
always @ (posedge clk or posedge reset) begin
if (reset) begin
cntArea_register <= 8'h0;
end else begin
cntArea_register <= (cntArea_register + 8'h01);
if(io_clear)begin
cntArea_register <= 8'h0;
end
end
end
endmodule
可以看到,我们定义的register寄存器在RTL代码中加上了Area名前缀cntArea。
END
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/vmI9qPolIKBrNFExEplDzA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。