LoginSignup
3
0

More than 3 years have passed since last update.

Chisel入門書「Digital Design with Chisel」2章の勉強記録

Last updated at Posted at 2019-09-11

記事の概要

Chiselの入門書「Digital Design with Chisel」の2章の勉強記録です。
本文の概要を備忘録として整理し、また実際に行った演習を紹介します。

本のpdfデータとプログラム一式は無料で以下から入手できます。
https://raw.githubusercontent.com/wiki/schoeberl/chisel-book/chisel-book.pdf
https://github.com/schoeberl/chisel-book

2.1 Signal Types and Constants

Chiselには3つのデータ型、Bits、UInt、SIntがあります。引数でビット幅を指定します。

Bits(8.W)    // 8ビットデータ
UInt(8.W)    // 8ビット符号なし整数
SInt(10.W)   // 10ビット符号付き整数

これらの型を用いて、信号、組み合わせ論理回路、およびレジスタを記述できます。例えば、1章のサンプルでは、LED用の1ビット出力信号ledを以下のように記述していました。

val led= Output(UInt(1. W))

定数データを記述する時は、引数なしのメソッド呼び出しを使います。

0.U  // UIntの定数0 
-3.S // SIntの定数-3

メソッドの引数にビット幅を指定することもできます。

8.U(4.W) // UIntの4-bit定数8

例えば、1章のサンプルでは、レジスタを32ビットの定数0で初期化する際、以下のように記述していました。

val cntReg = RegInit(0.U(32.W))

ビット幅を省略しても、自動で幅を推測してくれるので、以下のようなビット幅なしでの記述が可能です。

255.U           // UIntの定数255
"hff".U         // UIntの定数255の16進数表現
"o377".U        // UIntの定数255の8進数表現 
"b1111_1111".U  // UIntの定数255の2進数表現

.Uや.SはChiselのデータ型なので、これらのメソッド呼び出しはchisel3のパッケージを以下のようにimportしていないとエラーになります。

import chisel 3._

Chiselでは論理値のBool型は以下のように定義します。

Bool() 
true.B 
false.B

Chiselで論理値を使用する際は、Scalaのtrueやfalseを.Bのメソッド呼び出しでChiselのBool型に変換しないといけません。

2.2 Combinational Circuits

  • 組み合わせ回路の記述例
val logic = (a & b) | c
  • 論理演算子一覧
val and = a & b 
val or = a | b  
val xor = a ˆ b  
val not = ˜a 
  • 算術演算子一覧
val add = a + b // addition 
val sub = a - b // subtraction 
val neg = -a // negate 
val mul = a * b // multiplication 
val div = a / b // division 
val mod = a % b // modulo operation
  • 信号のタイプを先に定義してから、:=更新演算子を使用して、信号に値を割り当てることもできます。
val w = Wire(UInt())
w := a & b
  • 1ビットの抽出
val sign = x(31)
  • ビットの抜き出し範囲の指定
val lowByte = largeWord(7, 0)
  • 信号の結合
val word = Cat(highByte, lowByte)

2.2.1 Multiplexer

  • 2:1マルチプレクサ
val result = Mux(sel, a, b)

selは選択信号であり、ChiselのBool型です。
selがtrue.Bならば、信号yは信号aになります。
selがfalse.Bならば、信号yは信号bになります。

2.3 Registers

レジスタはD-フリップフロップの集合です。
Chiselではレジスタを記述すると、暗黙裡にクロックと接続されるので、わざわざクロックを記述する必要はありません。
クロックの立ち上がりでデータ更新されます。
(備忘録:ここまで読んで、立ち下がりエッジや両エッジでデータ更新したい場合は、どうすればいいのか疑問に思いました。)

  • リセット時に0で初期化される8ビットレジスタの例
val reg = RegInit(0.U(8.W))
  • 入力信号は:=更新演算子によりレジスタに接続され、レジスタの出力は式の名前を使用します。
reg := d 
val q = reg

qはリセット時には0で、クロックの立ち上がりごとに、1サイクル前のdの値を出力するフリップフロップになります。

  • レジスタを定義する段階で入力信号と接続することもできます
val nextReg = RegNext(d)

nextReg はクロックの立ち上がりごとに、1サイクル前のdの値を出力するフリップフロップになります。
この式では、リセット時には値が不定になります。

  • レジスタを定義する段階で入力信号と接続し、初期値を与えることもできます
val bothReg = RegNext(d, 0.U)

bothReg はリセット時には0で、クロックの立ち上がりごとに、1サイクル前のdの値を出力するフリップフロップになります。

2.3.1 Counting

  • 1から10までを繰り返しカウントするカウンター
val cntReg = RegInit(0.U(8.W))
cntReg := Mux(cntReg === 9.U, 0.U, cntReg + 1.U)

2.4 Structure with Bundle and Vec

異なるタイプの複数の信号をまとめるのにはBundleを用います。

class Channel() extends Bundle { 
  val data = UInt(32.W) 
  val valid = Bool() 
}

Bundleのフィールド(定義した各信号のこと)を使用するには、newでBundle を作成し、Wireオブジェクトを使用します。

val ch = Wire(new Channel()) 

C++言語においてクラスのメンバ変数を使用するのと同じように、フィールドにはドット表記でアクセスします。

ch.data := 123.U 
ch.valid := true.B

バンドルはフィールドだけを参照できます。

val b = ch.valid

また、全体を参照することもできます。

val channel = ch

同じタイプの複数の信号をまとめるのにはVecを用います。
マルチプレクサーを作成する場合は、Wireオブジェクトを使用します。

val v = Wire(Vec(3, UInt(4.W)))

個々の要素には(インデックス)を使用してアクセスします。

v(0) := 1.U 
v(1) := 3.U 
v(2) := 5.U

レジスタの配列を作成する場合は、Regオブジェクトを使用します。
例えば、64ビット幅の32ビットのレジスタファイルは以下のようになります。

val registerFile = Reg(Vec(32, UInt(64.W)))

レジスタファイルの要素には、インデックスを使用してアクセスし、通常のレジスタとして使用されます。

registerFile(idx) := dIn 
val dOut = registerFile(idx)

VecにBundleを含めることもできます。

val vecBundle = Wire(Vec(8, new Channel()))

BundleにVecを含めることもできます。

class BundleVec extends Bundle { 
  val field = UInt(8.W) 
  val vector = Vec(4,UInt(8.W)) 
}

Bundleタイプのレジスタにリセットによる初期値が必要な場合は、まずBundleを作成し、個々のフィールドの初期値を設定してから、

val initVal = Wire(new Channel())
initVal.data := 0.U 
initVal.valid := false.B

このBundleをRegInitに渡します。

val channelReg = RegInit(initVal)

2.5 Chisel Generates Hardware

Chiselからハードウェアを作成する際の諸注意が述べられています。

2.6 Exercise

演習にはFPGAボードにArty S7を使用しました。vivadoでのFPGAボードの使用方法は1章の記事を参照ください。

プロジェクトの作成

本では1章のプログラムを修正するように指示されていましたが、ここでは新規にプロジェクトを作成しました。

サンプルプロジェクトをコピーして、不要なファイルを削除します。

git clone https://github.com/ucb-bar/chisel-template.git Chap2
cd Chap2
rm -r src/main/scala/* src/test/scala/*
rm -rf .git 
git init 
git add .gitignore *

build.sbatのプロジェクト名を修正します。

name := "chisel-module-template" 

上記を以下に修正。

name := "Chap2" 

verilogに変換する際は、Chap2フォルダの階層にてsbtを起動し、runMainを実行します

sbt
sbt:Chap2> runMain Chap2

制約ファイル

今回の制約ファイルは以下になります。使用方法は、1章の記事を参照ください。

## This file is a general .xdc for the Arty S7-25 Rev. E
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock Signals
set_property -dict { PACKAGE_PIN F14   IOSTANDARD LVCMOS33 } [get_ports { clock }]; #IO_L13P_T2_MRCC_15 Sch=uclk
create_clock -add -name sys_clk_pin -period 83.333 -waveform {0 41.667} [get_ports { clock }];

## Switches
set_property -dict { PACKAGE_PIN H14   IOSTANDARD LVCMOS33 } [get_ports { io_sw[0] }]; #IO_L20N_T3_A19_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN H18   IOSTANDARD LVCMOS33 } [get_ports { io_sw[1] }]; #IO_L21P_T3_DQS_15 Sch=sw[1]
set_property -dict { PACKAGE_PIN G18   IOSTANDARD LVCMOS33 } [get_ports { io_sw[2] }]; #IO_L21N_T3_DQS_A18_15 Sch=sw[2]

## LEDs
set_property -dict { PACKAGE_PIN E18   IOSTANDARD LVCMOS33 } [get_ports { io_led }]; #IO_L16N_T2_A27_15 Sch=led[2]

## Buttons
set_property -dict { PACKAGE_PIN G15   IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L18N_T2_A23_15 Sch=btn[0]

## Configuration options, can be used for all designs
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]

## SW3 is assigned to a pin M5 in the 1.35v bank. This pin can also be used as
## the VREF for BANK 34. To ensure that SW3 does not define the reference voltage
## and to be able to use this pin as an ordinary I/O the following property must
## be set to enable an internal VREF for BANK 34. Since a 1.35v supply is being
## used the internal reference is set to half that value (i.e. 0.675v). Note that
## this property must be set even if SW3 is not used in the design.
set_property INTERNAL_VREF 0.675 [get_iobanks 34]

スイッチによるLEDのON/OFF

以下をFPGAに書き込むと、スイッチSW0のON/OFFに連動してLD2がON/OFFします。

/*
 * This code is a minimal hardware described in Chisel.
 * 
 * Copyright: 2013, Technical University of Denmark, DTU Compute
 * Author: Martin Schoeberl (martin@jopdesign.com)
 * License: Simplified BSD License
 * 
 */

import chisel3._
import chisel3.Driver

class Chap2 extends Module {
  val io = IO(new Bundle {
    val sw = Input(UInt(3.W))
    val led = Output(UInt(1.W))
  })

  val blkReg = RegInit(0.U(1.W))

  blkReg := io.sw
  io.led := blkReg
}

/**
 * An object extending App to generate the Verilog code.
 */
object Chap2 extends App {
  chisel3.Driver.execute(Array[String](), () => new Chap2())
}

2つのスイッチによるAND機能の実装

以下をFPGAに書き込むと、スイッチSW0とSW1を両方ともONにした場合のみ、LD2が点灯します。

/*
 * This code is a minimal hardware described in Chisel.
 * 
 * Copyright: 2013, Technical University of Denmark, DTU Compute
 * Author: Martin Schoeberl (martin@jopdesign.com)
 * License: Simplified BSD License
 * 
 */

import chisel3._
import chisel3.Driver

class Chap2 extends Module {
  val io = IO(new Bundle {
    val sw = Input(UInt(3.W))
    val led = Output(UInt(1.W))
  })

  val blkReg = RegInit(0.U(1.W))

  val and = io.sw(0) & io.sw(1)
  blkReg := and
  io.led := blkReg 
}

/**
 * An object extending App to generate the Verilog code.
 */
object Chap2 extends App {
  chisel3.Driver.execute(Array[String](), () => new Chap2())
}

3つのスイッチによる2:1マルチプレクサ

以下をFPGAに書き込むと、スイッチSW2をONにした場合、SW0のON/OFFに連動してLD2がON/OFFします。また、スイッチSW2をOFFにした場合、SW1のON/OFFに連動してLD2がON/OFFします。

/*
 * This code is a minimal hardware described in Chisel.
 * 
 * Copyright: 2013, Technical University of Denmark, DTU Compute
 * Author: Martin Schoeberl (martin@jopdesign.com)
 * License: Simplified BSD License
 * 
 */

import chisel3._
import chisel3.Driver

class Chap2 extends Module {
  val io = IO(new Bundle {
    val sw = Input(UInt(3.W))
    val led = Output(UInt(1.W))
  })

  val blkReg = RegInit(0.U(1.W))

  val result = Mux(io.sw(2), io.sw(0), io.sw(1))
  blkReg := result 
  io.led := blkReg 
}

/**
 * An object extending App to generate the Verilog code.
 */
object Chap2 extends App {
  chisel3.Driver.execute(Array[String](), () => new Chap2())
}

参考

関連記事

Chisel入門書「Digital Design with Chisel」1章の勉強記録
Chisel入門書「Digital Design with Chisel」3章の勉強記録
Chisel入門書「Digital Design with Chisel」4章の勉強記録
Chisel入門書「Digital Design with Chisel」5章の勉強記録
Chisel入門書「Digital Design with Chisel」6章の勉強記録
Chisel入門書「Digital Design with Chisel」7章の勉強記録
Chisel入門書「Digital Design with Chisel」8章の勉強記録
Chisel入門書「Digital Design with Chisel」9章の勉強記録

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0