十二 · 2021年11月03日

SpinalHDL—Area

在我们编写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
微信公众号:
 title=

推荐阅读

更多SpinalHDL技术干货请关注Spinal FPGA专栏。
推荐阅读
关注数
1578
内容数
130
用SpinalHDL提升生产力
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息