IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

HaskellでFPGAの最小のサンプル

入力を反転するだけのコード

Not gate

コンパイル

$ stack exec -- clash --interactive
CLaSHi, version 0.99 (using clash-lib, version 0.99):
http://www.clash-lang.org/  :? for help
Clash.Prelude> :cd ../worka
Clash.Prelude> :! ls
Clash01.hs
Clash.Prelude> :l Clash01.hs
[1 of 1] Compiling Clash01          ( Clash01.hs, interpreted )
Ok, modules loaded: Clash01.
*Clash01>

Verilog生成

*Clash01> :verilog
Loading dependencies took 2.500923s
Compiling: Clash01.topEntity
Applied 64 transformations
Normalisation took 1.056631s
Netlist generation took 0.006391s
Compiling: Clash01.testBench
Applied 219 transformations
Testbench normalisation took 0.550645s
Testbench netlist generation took 0.030401s
Total compilation took 4.170923s
*Clash01> :! ls
Clash01.dyn_hi  Clash01.dyn_o   Clash01.hi  Clash01.hs  Clash01.o   verilog
*Clash01> :! tree
.
├── Clash01.dyn_hi
├── Clash01.dyn_o
├── Clash01.hi
├── Clash01.hs
├── Clash01.o
└── verilog
    └── Clash01
        ├── Clash01_testBench
        │   ├── Clash01_outputVerifier.v
        │   ├── Clash01_stimuliGenerator.v
        │   └── Clash01_testBench.v
        ├── Clash01_testBench.vcd
        ├── Clash01_topEntity.manifest
        ├── Clash01_topEntity.v
        └── main.exe

3 directories, 12 files
*Clash01>

波形ファイルを作るためにテストベンチを修正

A Testbench

波形ファイル生成

$ find . -name "*.v" | xargs iverilog -o main.exe
$ ./main.exe
VCD info: dumpfile Clash01_testBench.vcd opened for output.

波形ファイルの確認

f:id:naotoogawa:20180114173923p:plain

ターゲット指定のcabalのrepl

cabalファイルで複数のターゲットを作り、cabal replすると起動ができなくなります。

$ cabal repl
cabal: The 'repl' command does not support multiple targets at once.
$

また、ターゲットの名前が重複すると起動しません。

$ cabal repl  myprotobuf
cabal: Ambiguous build target 'myprotobuf'. It could be:
exe:myprotobuf (component)
lib:myprotobuf (component)

この場合、cabal repl 種別:名称で指定します。

$ cabal repl exe:myprotobuf
$ cabal repl lib:myprotobuf
$ cabal repl test:MyTestSuite01

cabalファイルで記載する場所の例は下記です。

name:                myprotobuf
version:             0.1.0.0
...
executable myprotobuf    ### ここの名称
  main-is:             TCP01.hs
  other-extensions:    BangPatterns, DeriveDataTypeable, DeriveGeneric, FlexibleInstances, MultiParamTypeClasses
...
library lib-xproto   ### ここの名称
...
  exposed-modules:      DataBase.MySQLX.CRUD
...
Test-Suite MyTestSuite01   ### ここの名称
  type: exitcode-stdio-1.0

Haskellのドキュメント生成

haddockコマンド自体はディレクトリを指定してその配下にあるhsファイルを再帰的に処理してくれるオプションはないようです。xargsを使うのが手っ取り早いようです。

haskell - how to generate documentations with haddock? - Stack Overflow

$ find . -name '*.hs' | xargs haddock --html -o doc_dir

Implicit Parameters

Clashのプログラミングでサンプルを作り始めたところ、?変数名 :: 型というシグネチャが出てきました。

*> :t notM
notM
  :: (?rst::Reset domain synchronous, ?clk::Clock domain gated) =>
     Signal domain Bool -> Signal domain Bool

Implicit Paremtersという機能でした。

https://www.haskell.org/hugs/pages/users_guide/implicit-parameters.html

Readerモナドに似た挙動をするようです。

まずは、このような関数を考えてみます。

*> let foo = (*2) . (+2)
*> :t foo
foo :: Num c => c -> c
*> foo 1
6
*> foo 2
8
*> foo 3
10

次に、上の関数の実装内で2つあった関数((*2)(+2))の1番目の関数を、パラメータとして外出しします。

*> let foo' z = z . (+2)
*> :t foo'
foo' :: Num b => (b -> c) -> b -> c
*> foo' (*2) 1
6
*> foo' (*2) 2
8
*> foo' (*2) 3
10

さらに、上の関数で外出ししたパラメータを暗黙化します。

*> let foo'' = undefined :: (Num a, ?z::a->a) => a -> a
*> let foo'' = let ?z=(*2) in ?z . (+2)  :: (Num a, ?z::a->a) => a -> a
*> foo'' 1
6
*> foo'' 2
8
*> foo'' 3
10

参考情報

24 Days of GHC Extensions: Implicit Parameters

2018/01時点のClash

Githubから取得してclashをインストールした場合、2018/01時点では、hackageにあるチュートリアルはのコード動かないようです。

CLaSH.Tutorial

tutorialにあるソースをコンパイルするとパッケージがないとのエラーメッセージがでます。

Clash.Prelude> :l MAC.hs
[1 of 1] Compiling MAC              ( MAC.hs, interpreted )

MAC.hs:3:1: error:
    Could not find module ‘CLaSH.Prelude’
    Perhaps you meant Clash.Prelude (from clash-prelude-0.99)
    Use -v to see a list of the files searched for.
  |
3 | import CLaSH.Prelude
  | ^^^^^^^^^^^^^^^^^^^^
Failed, modules loaded: none.

CLasH.PreludeClash.Preludeに変更してコンパイルしてもエラーになります。

Clash.Prelude> :l MAC.hs
[1 of 1] Compiling MAC              ( MAC.hs, interpreted )

MAC.hs:14:14: error:
    • Expecting one more argument to ‘Signal (Signed 9, Signed 9)’
      Expected a type, but
      ‘Signal (Signed 9, Signed 9)’ has kind
      ‘* -> *’
    • In the type signature:
        topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |
14 | topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^

MAC.hs:14:21: error:
    • Expected kind ‘Domain’, but ‘(Signed 9, Signed 9)’ has kind ‘*’
    • In the first argument of ‘Signal’, namely ‘(Signed 9, Signed 9)’
      In the type signature:
        topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |
14 | topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |                     ^^^^^^^^^^^^^^^^^^^^

MAC.hs:14:45: error:
    • Expecting one more argument to ‘Signal (Signed 9)’
      Expected a type, but ‘Signal (Signed 9)’ has kind ‘* -> *’
    • In the type signature:
        topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |
14 | topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |                                             ^^^^^^^^^^^^^^^^^

MAC.hs:14:53: error:
    • Expected kind ‘Domain’, but ‘Signed 9’ has kind ‘*’
    • In the first argument of ‘Signal’, namely ‘(Signed 9)’
      In the type signature:
        topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |
14 | topEntity :: Signal (Signed 9, Signed 9) -> Signal (Signed 9)
   |                                                     ^^^^^^^^
Failed, modules loaded: none.

どうやら、諸々APIが変わっているようです。

サンプルは、リポジトリから取得した中にexampleがあるのでそれを使います。

clash-compiler/examples at master · clash-lang/clash-compiler · GitHub

起動

$ stack exec -- clash --interactive
CLaSHi, version 0.99 (using clash-lib, version 0.99):
http://www.clash-lang.org/  :? for help

サンプルの確認

Clash.Prelude> :! ls
MAC.hs
*MAC> :! cat MAC.hs
module MAC where

import Clash.Prelude

ma acc (x,y) = acc + x * y

macT acc (x,y) = (acc',o)
  where
    acc' = ma acc (x,y)
    o    = acc

mac = macT `mealy` 0

topEntity
  :: SystemClockReset
  => Signal System (Signed 9,Signed 9)
  -> Signal System (Signed 9)
topEntity = mac
{-# NOINLINE topEntity #-}

testBench :: Signal System Bool
testBench = done'
  where
    testInput      = stimuliGenerator $(listToVecTH [(1,1) :: (Signed 9,Signed 9),(2,2),(3,3),(4,4)])
    expectedOutput = outputVerifier $(listToVecTH [0 :: Signed 9,1,5,14])
    done           = expectedOutput (topEntity testInput)
    done' = withClockReset (tbSystemClockGen (not <$> done')) systemResetGen done
*MAC>

ロード(コンパイル)

Clash.Prelude> :l MAC.h
MAC.hi  MAC.hs
Clash.Prelude> :l MAC.hs
[1 of 1] Compiling MAC              ( MAC.hs, interpreted ) [flags changed]
Ok, modules loaded: MAC.

verilog生成

*MAC> :verilog
Loading dependencies took 2.235453s
Compiling: MAC.topEntity
Applied 133 transformations
Normalisation took 1.062433s
Netlist generation took 0.011033s
Compiling: MAC.testBench
Applied 425 transformations
Testbench normalisation took 0.557544s
Testbench netlist generation took 0.013983s
Total compilation took 3.906076s
*MAC>

生成内容の確認

*MAC> :! tree verilog/
verilog/
└── MAC
    ├── MAC_testBench
    │   ├── MAC_outputVerifier.v
    │   ├── MAC_stimuliGenerator.v
    │   └── MAC_testBench.v
    ├── MAC_topEntity.manifest
    └── MAC_topEntity.v

2 directories, 5 files
*MAC>

FPGAプログラミング大全 Xilinx編でZyboを使うとはまるところ

FPGAプログラミング大全 Xilinx編

FPGAプログラミング大全 Xilinx編

5章、6章はソフトマクロCPU MicroBlaze の解説になっています。その解説は、ArtyとBasys3向けの説明になっており、Zyboを使う場合は、適宜読み替える必要があります。Zybo用の設定はAppendixに記載されているのでそれに従えばよいです。ですが、書籍には明示的に記載されていない読み替え(実際は、読み飛ばし!)が必要でした。

書籍の手順通りに進めると下記のようなメッセージがでました。

f:id:naotoogawa:20180107173726p:plain

メッセージの一部をテキスト化しました。

[Place 30-58] IO placement is infeasible. Number of unplaced terminals (1) is greater than number of available sites (0).
The following are banks with available pins: 
 IO Group: 0 with : SioStd: LVCMOS18   VCCO = 1.8 Termination: 0  TermDir:  In   RangeId: 1  has only 0 sites available on device, but needs 1 sites.
    Term: RXD
[Place 30-374] IO placer failed to find a solution
Below is the partial placement that can be analyzed to see if any constraint modifications will make the IO placement problem easier to solve.

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                     IO Placement : Bank Stats                                                                           |
+----+-------+-------+------------------------------------------------------------------------+------------------------------------------+--------+--------+--------+-----+
| Id | Pins  | Terms |                               Standards                                |                IDelayCtrls               |  VREF  |  VCCO  |   VR   | DCI |
+----+-------+-------+------------------------------------------------------------------------+------------------------------------------+--------+--------+--------+-----+
|  0 |     0 |     0 |                                                                        |                                          |        |        |        |     |
| 13 |     0 |     0 |                                                                        |                                          |        |        |        |     |
| 34 |    50 |     1 | LVCMOS33(1)                                                            |                                          |        |  +3.30 |    YES |     |
| 35 |    50 |     5 | LVCMOS33(5)                                                            |                                          |        |  +3.30 |    YES |     |
+----+-------+-------+------------------------------------------------------------------------+------------------------------------------+--------+--------+--------+-----+
|    |   100 |     6 |                                                                        |                                          |        |        |        |     |
+----+-------+-------+------------------------------------------------------------------------+------------------------------------------+--------+--------+--------+-----+

IO Placement:
+--------+----------------------+-----------------+----------------------+----------------------+----------------------+
| BankId |             Terminal | Standard        | Site                 | Pin                  | Attributes           |
+--------+----------------------+-----------------+----------------------+----------------------+----------------------+
| 34     | RST                  | LVCMOS33        | IOB_X0Y36            | Y16                  |                      |
+--------+----------------------+-----------------+----------------------+----------------------+----------------------+
| 35     | CLK                  | LVCMOS33        | IOB_X0Y78            | L16                  |                      |
|        | LED[0]               | LVCMOS33        | IOB_X0Y54            | M14                  |                      |
|        | LED[1]               | LVCMOS33        | IOB_X0Y53            | M15                  |                      |
|        | LED[2]               | LVCMOS33        | IOB_X0Y99            | G14                  |                      |
|        | LED[3]               | LVCMOS33        | IOB_X0Y93            | D18                  |                      |
+--------+----------------------+-----------------+----------------------+----------------------+----------------------+

端子の数が合わないってどういうことなんでしょうか?

結論から言えば、Zyboでは、UARTブロックの追加をする必要がないということでした。 5章P.195に「UARTを接続」がありますが、この部分をまるっと無視する必要があります。

理由は、Appendixに「Zyboボード上のUSBシリアルインターフェースは、PSのMIOに接続されているので、PL側の回路では使用できません。(中略)JTAG経由でシリアル出力しますので、RDXやTXDなどの端子は不要です。」と説明されています。

なのでブロック図は下記のようになります。

f:id:naotoogawa:20180107174440p:plain

UARTを接続をせずに手順通りに進めるとHelloWorldがだせました。

f:id:naotoogawa:20180107174516p:plain

記念にHello FPGA Worldとしてみました。

f:id:naotoogawa:20180107174528p:plain

つまらないところで、嵌ってしまいました。