• 基于Chisel的FPGA流水灯设计


    一、Chisel

    (一)什么是Chisel

    Chisel是一种基于Scala的硬件构造语言,它允许设计者使用高级的编程概念来构建硬件。Chisel的设计哲学是“不要重复自己”(DRY),这意味着它鼓励设计者通过参数化和模块化来重用代码。

    (二)Chisel能做什么

    在硬件设计领域,传统的硬件描述语言(如Verilog和VHDL)虽然功能强大,但它们通常比较底层,需要设计者关注很多细节。这不仅增加了设计的复杂性,也容易引入错误。而Chisel语言提供了一种更高层次的抽象,使得设计者可以专注于硬件的功能和行为,而不是具体的实现细节。

    (三)Chisel的使用

    1. 定义模块:
      在Chisel中,硬件模块被定义为Scala的类或对象,这些类或对象可以包含端口(IO)和内部逻辑。
    2. 构建行为:
      Chisel使用Scala的表达式来定义硬件的行为。例如,可以使用条件语句和循环来实现控制逻辑。
    3. 参数化设计:
      Chisel支持参数化设计,这意味着可以通过参数来定义模块的大小和功能,从而创建可重用的硬件组件。
    4. 生成硬件:
      在Chisel提中使用generateVerilog等工具来将高级的Chisel代码转换成传统的硬件描述语言。

    (四)Chisel的优缺点

    1.优点

    1. 硬件构建语言:

    Chisel专注于硬件设计,提供了专门的语法和功能来构建硬件。

    1. 内嵌于Scala:

    由于Chisel是Scala的一个特定领域语言,它能够利用Scala的高级特性,如面向对象编程、函数式编程等。

    1. 高度参数化:

    Chisel支持使用Scala的元编程来创建高度参数化的硬件设计,这有助于代码复用和设计灵活性。

    1. 代码复用:

    Chisel支持端口的嵌套、继承和重载,这使得端口代码复用变得高效,减少了定义和连接端口的工作量。

    1. 设计参数化:

    Chisel的设计参数化特性允许设计者创建可配置和可重用的硬件模块。

    1. 编译器结构:

    Chisel是硬件编译器框架的一部分,类似于LLVM在软件编译中的应用,这使得前后端解耦,允许编写其他前后端,并利用现有的中端优化和后端。

    1. 自动化特化/转换:

    Chisel的编译器结构允许电路的自动化特化和转换,例如将电路转换为FPGA优化版本,提高运行速度。

    2.缺点

    1. 学习曲线:

    对于习惯于使用传统硬件描述语言(如Verilog和VHDL)的工程师来说,Chisel可能有一个陡峭的学习曲线,因为它需要掌握Scala语言和Chisel特定的语法。

    1. 社区和工具支持:

    虽然Chisel正在获得越来越多的关注,但与Verilog和VHDL等成熟的硬件描述语言相比,它的社区和工具支持可能还不够广泛。

    1. 不支持某些结构:

    Chisel可能不支持Verilog中的所有结构,例如模拟元素,尽管它提供了通过Verilog黑匣子的逃生舱口来解决这个问题。

    1. 普及度:

    Chisel作为一种相对较新的语言,可能还没有在工业界得到广泛的普及和应用,这可能会影响到它的接受度和使用。

    二、流水灯设计

    先搭建好环境:chisel开发环境搭建(intellij)
    built.sbt

    name := "untitled2"
    
    version := "0.1"
    
    scalaVersion := "2.11.10"
    scalacOptions += "-Xsource:2.11"
    
    libraryDependencies ++= Seq(
      "edu.berkeley.cs" %% "chisel3" % "3.1.2",
      "edu.berkeley.cs" %% "chisel-iotesters" % "1.2.3",
      "org.scalatest" %% "scalatest" % "3.0.5" % "test"
    )
    
    

    FlowingLED.scala

    package flowing_led
    
    import chisel3._
    import chisel3.util._
    
    class FlowingLED extends Module {
      val io = IO(new Bundle {
        val led = Output(UInt(18.W))
      })
    
      // 计数器生成函数
      def genCounter(n: UInt): UInt = {
        val cntReg = RegInit(0.U(32.W))
        cntReg := Mux(cntReg === n - 1.U, 0.U, cntReg + 1.U)
        cntReg
      }
    
      val TIME_0_1S = 5000000.U
      val count = genCounter(TIME_0_1S)
      val shiftReg = Reg(UInt(18.W))  // 初始化寄存器为0,位宽为18
    
      // 移位逻辑
      when(!reset.toBool()){
        shiftReg := 0.U // 复位时初始化为0
      }.otherwise {
        when(count === (TIME_0_1S - 1.U)) {
          shiftReg := Cat(shiftReg(16, 0), ~shiftReg(17)) // 移位操作
        }
      }
    
      io.led := shiftReg
    }
    
    

    FlowingLEDNew.

    package flowing_led
    
    import chisel3.Driver
    
    object FlowingLEDNew extends App {
      val targetDir = "output_FlowingLED"
      Driver.execute(Array("--target-dir", targetDir), () => new FlowingLED())
    }
    
    

    在这里插入图片描述
    点击运行文件
    在这里插入图片描述
    用Quartus新建工程,添加FlowingLED.v
    因对chisel还不是很熟悉,由chisel生成的verilog中有些错误,
    所以需将

    always @(posedge clock) begin
      if (reset) begin
      改为
    always @(posedge clock) begin
      if (!reset) begin
    

    再加入测试文件
    flowing_led_tb

    `timescale 1ns / 1ps
    
    module flowing_led_tb;
    
      // 测试平台的输入输出定义
      reg clock;     // 时钟信号
      reg reset;     // 复位信号
      wire [17:0] io_led;  // LED 输出
    
      // 实例化被测试模块
     	FlowingLED uut (
        .clock(clock),
        .reset(reset),
        .io_led(io_led)
      );
    
      // 时钟信号生成
      always #10 clock = ~clock;  // 产生一个周期为20ns的时钟信号,即50MHz
    
      // 测试过程
      initial begin
        // 初始化输入信号
        clock = 0;
    	 reset = 0;
    	 
    	 #100;
    	 
        reset = 1; // 初始复位信号设为高
        
        // 等待一个时钟周期以稳定复位状态
        #1000000;
        
        // 释放复位信号
        reset = 0;
        
        // 等待一段时间观察 LED 的变化
        #100; // 等待1000ns,观察流水灯的初始状态
    
        
        // 结束仿真
    
      end
    
      // 监视信号
      initial begin
        $monitor("Time=%t, reset=%b, count=%d, io_led=%b, _T_28=%b, _T_24=%b, _GEN_2=%d",
                 $time, reset, uut.count, io_led, uut._T_28, uut._T_24, uut._GEN_2);
      end
    
    endmodule
    

    为方便测试可以将生成的count和GEN2改小一点:

     assign _T_9 = count == 32'h10; // @[FlowingLED.scala 26:26:@9.4]
     assign _GEN_2 = 32'h9;// @[FlowingLED.scala 15:17:@24.6]
    

    编译运行一下
    打开Modelsim进行仿真
    先运行

    vlog 自己的路径:/Flowing_led_tb.v
    vsim water_led_tb
    

    在运行run -all
    可以得到仿真结果:
    在这里插入图片描述
    在这里插入图片描述
    也可以看仿真出来的图在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    hive on spark下row_number()问题排查
    绑定点击事件及解绑点击事件
    【Azure 媒体服务】Azure Media Player 在Edge浏览器中不能播放视频问题的分析与解决
    Raven2靶机渗透
    uni-app基于vite和vue3创建并集成pinia实现数据持久化
    Chrome扩展的核心:manifest 文件(上)
    c++视觉处理---cv::Sobel()`算子
    基于Php幼儿园管理系统
    聊聊微服务治理体系
    .NET Core C#系列之XiaoFeng.Threading.JobScheduler作业调度
  • 原文地址:https://blog.csdn.net/Morzart/article/details/139365137