吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块

2023-11-19

Chisel模块详解(五)——Chisel中使用Verilog模块

上一篇文章讲述了用函数实现轻量级模块的方法,可以大幅度提升编码效率。Chisel中也提供了一些好用的函数,方便我们编写代码,也方便Chisel编译器优化生成的硬件电路。在Chisel中除了使用我们写的模块或函数硬件生成器,我们可能还需要使用现有的IP,而这些IP通常是用Verilog来写的,我们该如何使用这些IP呢?这一篇文章就来说说。

有时候我们需要在项目中使用一个模块,这个模块可能是个IP,或者是之前项目中比较成熟的模块,但通常都是Verilog描述的电路。又或者我们希望确保某个模块生成的Verilog代码有特定的结构,希望以此来让综合工具识别并映射到一个好用的原语。又或者有个硬件构造Chisel描述不了,只能用Verilog描述。再或者需要连接到Chisel中未定义的FPGA或者其他IP中。Chisel中就有BlackBoxExtModule这两个类帮我们在Chisel中使用Verilog代码描述的模块。

这两个类的使用是类似的,都需要用Map[String, Param]来参数化,用于转换为生成的Verilog代码中的模块参数。其中BlackBox以独立地Verilog文件发射,而ExtModule是类似占位符一样的存在,作为无源码的模块实例发射。这个特性使得ExtModule有时候很有用,比如针对Xilinx或Intel设备中类似时钟或输入缓存这类原语的时候。

先看一个BlackBox的例子,这里的BUFGCE是FPGA中的一个原语,是带有时钟使能端的全局缓冲,有一个时钟输入I,一个使能端CE以及一个输出O,注意BlockBox类和Module不同,不会隐式包含时钟和复位信号:

import chisel3._
import chisel3.util._

class BUFGCE extends BlackBox(Map("SIM_DEVICE" -> "7SERIES")) {
    val io = IO(new Bundle {
        val I = Input(Clock())
        val CE = Input(Bool())
        val O = Output(Clock())
    })
}

class Top extends Module {
    val io = IO(new Bundle {})
    val bufgce = Module(new BUFGCE)
    // 连接BUFGCE的时钟输入端口到顶层模块的时钟信号
    bufgce.io.I := clock
}

生成的Verilog代码如下:

module Top(
  input   clock,
  input   reset
);
  wire  bufgce_I; // @[hello.scala 18:24]
  wire  bufgce_CE; // @[hello.scala 18:24]
  wire  bufgce_O; // @[hello.scala 18:24]
  BUFGCE #(.SIM_DEVICE("7SERIES")) bufgce ( // @[hello.scala 18:24]
    .I(bufgce_I),
    .CE(bufgce_CE),
    .O(bufgce_O)
  );
  assign bufgce_I = clock; // @[hello.scala 20:17]
  assign bufgce_CE = 1'h0;
endmodule

可以看到顶层模块Top里面包含了一个用参数实例化了的BUFGCE,然后它的端口分别连接到了Top中对应的线网上。

ExtModule也是类似的,不过在Chisel 3.5中需要导入实验性包:

import chisel3._
import chisel3.util._
import chisel3.experimental.ExtModule

class alt_inbuf extends ExtModule(Map("io_standard" -> "1.0 V",
                                     "location" -> "IOBANK_1",
                                     "enable_bus_hold" -> "on",
                                     "weak_pull_up_resistor" -> "off",
                                     "termination" -> "parallel 50 ohms")) {
    val io = IO(new Bundle {
        val i = Input(Bool())
        val o = Output(Bool())
    })
}

class Top extends Module {
    val io = IO(new Bundle {})
    val inbuf = Module(new alt_inbuf)
}

输出的Verilog代码如下:

module Top(
  input   clock,
  input   reset
);
  wire  inbuf_io_i; // @[hello.scala 21:23]
  wire  inbuf_io_o; // @[hello.scala 21:23]
  alt_inbuf
    #(.termination("parallel 50 ohms"), .location("IOBANK_1"), .enable_bus_hold("on"), .io_standard("1.0 V"), .weak_pull_up_resistor("off"))
    inbuf ( // @[hello.scala 21:23]
    .io_i(inbuf_io_i),
    .io_o(inbuf_io_o)
  );
  assign inbuf_io_i = 1'h0;
endmodule

生成了这样的代码之后,我们只需要给出对应的模块的Verilog实现就能够使用了。

我们有三种方法给出对应的Verilog实现,比如对于下面的加法器的IO端口:

class BlockBoxAdderIO extends Bundle {
    val a = Input(UInt(32.W))
    val b = Input(UInt(32.W))
    val cin = Input(Bool())
    val c = Output(UInt(32.W))
    val cout = Output(Bool())
}

第一种方法是Chisel代码中内联Verilog代码:

class InlineBlackBoxAdder extends HasBlackBoxInline {
    val io = IO(new BlockBoxAdderIO)
    setInline("InlineBlackBoxAdder.v",
             s"""
             |module InlineBlackBoxAdder(a, b, cin, c, cout);
             |input [31:0] a, b;
             |input cin;
             |output [31:0] c;
             |output cout;
             |wire [32:0] sum;
             | 
             |assign sum = a + b + cin;
             |assign c = sum[31:0];
             |assign cout = sum[32];
             |
             |endmodule
             """.stripMargin)
}

class Top extends Module {
    val io = IO(new Bundle {})
    val adder = Module(new InlineBlackBoxAdder)
}

Verilog代码在这种方式中,以字符串字面值的形式给出,开头有个sf,然后三对双引号括起来的就是,用竖线|可以放格式很好看的Verilog代码。此外,这种方法也可以参数化,因为Scala变量可以用$${}插入到字符串里面。最后的stripMargin方法会在发射代码的时候移除竖线和制表符。

还有两种方法是直接导入Verilog源文件,需要Verilog源代码放在一个单独的文件里面,可以这么写:

class ResourceBlackBoxAdder extends HasBlackBoxResource {
    val io = IO(new BlackBoxAdderIO)
    addResource("/ResourceBlackBoxAdder.v")
}

还可以这么写:

class PathBlackBoxAdder extends HasBlackBoxPath {
    val io = IO(new BlackBoxAdderIO)
    addResource("./src/main/resources/ResourceBlackBoxAdder.v")
}

其中HasBlackBoxPath版本的写法要提供项目文件夹的相对路径,而HasBlackBoxResource会默认在./src/main/resources文件夹下搜索Verilog代码。

上面的例子输出的Verilog代码类似下面的代码:

module Top(
  input   clock,
  input   reset
);
  wire [31:0] adder_a; // @[hello.scala 37:23]
  wire [31:0] adder_b; // @[hello.scala 37:23]
  wire  adder_cin; // @[hello.scala 37:23]
  wire [31:0] adder_c; // @[hello.scala 37:23]
  wire  adder_cout; // @[hello.scala 37:23]
  InlineBlackBoxAdder adder ( // @[hello.scala 37:23]
    .a(adder_a),
    .b(adder_b),
    .cin(adder_cin),
    .c(adder_c),
    .cout(adder_cout)
  );
  assign adder_a = 32'h0;
  assign adder_b = 32'h0;
  assign adder_cin = 1'h0;
endmodule

使用emitVerilog发射Verilog代码时,还会生成一个InlineBlackBoxAdder.v的实现:

module InlineBlackBoxAdder(a, b, cin, c, cout);
input [31:0] a, b;
input cin;
output [31:0] c;
output cout;
wire [32:0] sum;
 
assign sum = a + b + cin;
assign c = sum[31:0];
assign cout = sum[32];

endmodule

BlackBox和其他模块的实例化是一样的,用个Module封装就行了,即Module(new BlackBoxModule),前面的例子已经展示过。但是BlackBox类是不能直接测试的,必须要封装到测试代码中的一个命名类或匿名类中,这两种方法都允许和BlackBox有相同的IO端口:

class InlineAdder extends Module {
    val io = IO(new BlackBoxAdderIO)
    val adder = Module(new InlineBlackBoxAdder)
    io <> adder.io
}

或者:

test(new Module {
    val io = IO(new BlackBoxAdderIO)
    val adder = Module(new InlineBlackBoxAdder)
    io <> adder.io
})

注意,HasBlackBoxInlineHasBlackBoxResourceHasBlackBoxPath这三个类都是从Chisel的BlackBox类拓展出来的traits(特质),也就是说class Example extends BlackBox with HasBlackBoxInlineclass Example extends HasBlackBoxInline是等价的。

结语

这一篇文章解决了Chisel中使用Verilog代码的问题,在项目中需要的时候可以通过BlackBox类型使用Verilog模块,将相应的模块与相应的接口连接起来就可以了。关于Chisel模块的内容到这里就结束了,下一大部分我们会一起学习Chisel中的组合电路的相关语法,敬请期待!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块 的相关文章

  • 「Verilog学习笔记」游戏机计费程序

    专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点 刷题网站用的是牛客网 timescale 1ns 1ns module game count input rst n 异位复位信号 低电平有效 input clk 时
  • 基于FPGA的简易BPSK和QPSK

    1 框图 2 顶层 3 m generator M序列的生成 输出速率为500Kbps 4 S2P是串并转换模块 将1bit的m序列转换到50M时钟下的2bit M序列数据 就有4个象限 5 my pll是生成256M的时钟作为载波 因为s
  • x 和 z 值在 Verilog 中到底代表什么?

    Verilog 标准定义了四种类型的位值 0 1 x 和 z 其中 0 表示低 1 表示高 x 表示未知 z 表示未驱动网络 有几个问题 x 是否意味着我们不知道该值是 0 还是 1 0 或 1 或 z 或者该值是未知的并且可以是 0 1
  • 如何在RTL中使用时钟门控?

    我正在对一些时钟进行门控latch以及我设计中的逻辑 我在综合和布局布线方面没有太多经验 在 RTL 中实现时钟门控的正确方法是什么 示例1 always comb begin gated clk clk latch update en e
  • Vivado ILA的debug信息保存与读取

    保存 write hw ila data D Project FPGA ILA Debug Data 202401041115 ila upload hw ila data hw ila 1 读取 display hw ila data r
  • 在逻辑中使用单端端口期待差异对?

    我使用的逻辑被设置为需要一个差分对时钟端口 然而 对于一个特定的应用程序 我只能输入一个单端时钟 由于硬件限制 修改逻辑以接受单端时钟不是一种选择 因为涉及许多文件和代码行 有没有办法可以输入单端端口并以某种方式将其馈送到模块的差异对端口
  • verilog 中的“<<”运算符

    我有一个verilog代码 其中有一行如下 parameter ADDR WIDTH 8 parameter RAM DEPTH 1 lt lt ADDR WIDTH 这里将存储什么RAM DEPTH以及什么是 lt lt 操作员在这里做
  • TRICONEX MA2211-100 芯片上相互连接

    TRICONEX MA2211 100 芯片上相互连接 TRICONEX MA2211 100 所有相同的组件 io的电源 处理器 和内存将需要 但是 你可以看到所有这些带存储器和处理器的OO板 针不能嵌入到一个小的单片机上 现在是 普拉克
  • 为什么 RV64 为 32 位操作而不是 64 位操作引入新的操作码

    在浏览 RISC V 规范时 我注意到 64 位版本与 32 位版本的不同之处在于 它 将寄存器扩展至 64 位 更改了指令以作用于整个 64 位范围 添加了执行 32 位操作的新指令 这使得 RV32 代码与 RV64 不兼容 但是 如果
  • RISC-V 中的旋转位

    嘿 我对 RISC V 还算陌生 我的练习题之一是 将 0x0000000000000123 的值右移 4 位 预期结果为 0x3000000000000012 即所有十六进制数字向右移动一位 而最右边的一位移动到前面 到目前为止 我了解了
  • RISC V手册混淆:指令格式VS立即数格式

    我有一些与 RISC V 手册相关的问题 它有不同类型的指令编码 如R型 I型 就像MIPS编码一样 R type 31 25 24 20 19 15 14 12 11 7 6 0 funct7 rs2 rs1 funct3 rd opco
  • if 语句导致 Verilog 中的锁存推断?

    我正在编写用于合成算法的 Verilog 代码 我对哪些情况可能导致推断锁存器有点困惑 下面是这样的一段代码 虽然它在模拟中工作得很好 但我担心它可能会导致硬件问题 always b1 or b2 b1 map b2 map m1 map
  • 如何使用 don't cares 参数化 case 语句?

    我有一条称为输入的电线 我想检测前导的数量 我正在尝试创建一个模块 该模块使用下面的 case 语句根据前导零的数量更改输出数据 然而 输入的大小是可参数化的 如果 X 是固定值 4 我将创建一个 case 语句 case input 4
  • 为定制 RISC-V imafd SOC 移植 Linux

    我正在尝试构建一个 yocto演示 coreip cli我的自定义 risc v SOC 的图像仅支持imafd指示 对于 Bitbake 使用的交叉工具链的编译 我尝试更改 openembedded core 层中的 cross binu
  • 零/符号扩展是无操作的,为什么要为每种大小类型提供指令呢?

    对于 x86 和 x64 编译器生成类似的零 符号扩展 MOVSX 和 MOVZX 扩展本身并不是免费的 但允许处理器执行无序魔法加速 但在 RISC V 上 因此 无符号和有符号 32 位整数之间的转换是无操作 从有符号 32 位整数到有
  • 在 Verilog 设计中产生时钟故障

    我正在使用 Verilog 设计芯片 我有一个 3 位计数器 我希望当计数器处于第 8 次循环时 应该有一个时钟故障 之后就可以正常工作了 在 Verilog 设计中产生时钟故障的可能方法是什么 在时钟信号上注入毛刺的一种方法是使用forc
  • Verilog 中的 If 语句和分配连线

    我试图弄清楚基于组合逻辑分配电线的基础知识 I have wire val wire x wire a wire b always begin if val 00 I want to assign x a if val 01 I want
  • RISC-V 调用约定的 ABI 寄存器名称

    我对 RISC V ABI 寄存器名称感到困惑 例如 第 85 页的 RISC V 指令集手册 第一卷 用户级 ISA 版本 2 0 中的表 18 2 指定堆栈指针sp正在注册x14 然而 指令 addi sp zero 0 由 riscv
  • Verilog 双向握手示例

    我正在完成一个项目 要求是处理器内部功能单元之间的双向握手 我知道它是什么 但是有没有任何 标准 或一个简单的例子 我唯一能想到的就是两个单元之间 当它们之间有一条数据线并且当 X 发送到 Y 时 会给出一个单独的 发送 信号 当 Y 接收
  • 学习 Verilog 的资源 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我是 Verilog 新手 有人可以推荐学习资源 书籍 视频 博客或任何他们有良好个人经验并帮助他们更

随机推荐

  • 3、思科模拟器介绍 (认识思科模拟器界面、安装思科模拟器、思科模拟器汉化)

    认识思科模拟器界面 标题栏 菜单栏 思科模拟器软件包 CSDN思科模拟器安装 https download csdn net download weixin 53645521 85135225 百度网盘思科模拟器安装包 链接 https p
  • 图像恢复(加噪与去噪)

    人工智能导论实验导航 实验一 斑马问题 https blog csdn net weixin 46291251 article details 122246347 实验二 图像恢复 https blog csdn net weixin 46
  • tar命令笔记

    作用 tar 可以保存文件属性 本身不具备压缩能力 配合gzip或者bzip 进行压缩解压缩 参数 相关参数如下 来自百度百科 c create 创建新的tar文件 x extract get 解开tar文件 t list 列出tar文件中
  • 火狐浏览器文本两端对齐无效text-align: justify

    找了很多地方 尝试很多办法都不好使 直到看到这篇 只需要设置了text align justify时加设一个white space pre line就可以了
  • [Docker]使用Docker部署Kafka

    Kafka 是一个分布式流处理平台 它依赖于 ZooKeeper 作为其协调服务 在 Kafka 集群中 ZooKeeper 负责管理和协调 Kafka 的各个节点 因此 要在 Docker 容器中启动 Kafka 通常需要同时启动一个 Z
  • 对数器的简单使用

    对数器 1 前言 2 内容 简介对数器 以排序算法的检测为实例 3 总结 4 更新日志 1 前言 学习左神的数据结构的过程中 推荐使用对数器检验自己的算法是否正确 2 内容 简介对数器 1 对数器的作用 在一个题目未OJ的时候 可以通过对数
  • Transformer学习笔记

    一 Transformer诞生背景 Transformer模型是解决序列转录问题的一大创新 在Transformer模型之前 序列转录模型都或多或少的基于复杂的循环或卷积神经网络 循环神经网络的计算是时序性的 位置的计算必须基于之前所有位置
  • 微信小程序数据 \n 换行符失效解决办法

    最近遇到一个问题 使用uni app写小程序时 拿到一个字符串 后台返回的 需要在 n 处换行 但是直接使用 let title 黄鹤楼送 n孟浩然之广陵
  • 使用python对银行信息管理系统的简单实现

    一 首先是用户属性的类 class account object 储存用户信息的类 def init self id1 name tel money self id id1 账户 self name name 姓名 self tel tel
  • mo管理器java_Android开发之通过包管理器获取安装应用信息

    最近在自己写一个APP 有一个模块需要获取手机应用的一些信息 坑还是有 但都基本踩过了 自己把他实现了出来 实现方法还是很需要掌握的 底部弹出的对话框中四个选项的实现不多做说明 主要讲讲如何获取这些安装的应用信息 好了 不多说 看看效果图
  • 1024,干程序才懂得节日!

    1024程序员节 1024程序员节是广大程序员的共同节日 1024是2的十次方 二进制计数的基本计量单位之一 针对程序员经常周末加班与工作日熬夜的情况 部分互联网机构倡议每年的10月24日为1024程序员节 在这一天建议程序员拒绝加班 程序
  • 【C/C++】报错问题积累

    1 出现Deprecated declaration XXX give arg types c文件中 有没有参数的函数时 声明需要加void即 main c void fun main h void fun void
  • androidX 在AndroidMainfest里面加入provider后编译不通过

  • 【three.js练习程序】创建简单物理地形

  • ubuntu 18.04 双系统安装

    下载镜像 Ubuntu 18 04 6 LTS Bionic Beaver 磁盘分区用于ubuntu存储 在C盘中分出200M用于ubuntu的引导启动 C盘已经分出200M空间 D盘分配出160G用于存储文件 U盘制作系统盘 刻录软件 推
  • linux下TUN或TAP虚拟网卡的使用

    tun tap 驱动程序实现了虚拟网卡的功能 tun表示虚拟的是点对点设备 tap表示虚拟的是以太网设备 这两种设备针对网络包实施不同的封装 利用tun tap 驱动 可以将tcp ip协议栈处理好的网络分包传给任何一个使用tun tap驱
  • ClickHouse安装(集群版)

    ClickHouse安装 集群版 一 准备工作 1 设置hostname 2 hosts映射 3 关闭防火墙 4 同步时间 5 关闭selinux 6 安装好zookeeper 7 重启 二 搭建ClickHouse集群 1 下载安装包 2
  • c++类模板与继承详解

    c 类模板 继承 详解 类模板和类模板之间 类模板和类之间可以互相继承 它们之间的派生关系有以下四种情况 1 类模板继承类模板 2 类模板继承模板类 3 类模板继承普通类 4 普通类继承模板类 include
  • 【linux】shell 编程之字符串与数组

    前言 对字符串的操作在众多的编程语言中可以说是最基础的了 字符串 String 就是一系列字符的组合 字符串是 Shell 编程中最常用的数据类型之一 除了数字和字符串 也没有其他类型了 一 shell 中字符串的几种格式 在shell中
  • 吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块

    Chisel模块详解 五 Chisel中使用Verilog模块 上一篇文章讲述了用函数实现轻量级模块的方法 可以大幅度提升编码效率 Chisel中也提供了一些好用的函数 方便我们编写代码 也方便Chisel编译器优化生成的硬件电路 在Chi