Added a PWM output control to VexRiscv (RISC-V) Murax Soc.
先日、VexRiscv Murax SoC を TinyFPGA-BX で動かす修正を御紹介しましたが、今回は自分で書いた小さな小さなペリフェラル(PWM 出力)を APB3 バス経由で Murax SoC に繋いでみました。
SpinalHDL によるコードは非常にシンプルで明解です。じゃん。
package flogics.vexriscv.pwm
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config, Apb3SlaveFactory}
class Pwm(size: Int) extends Component {
val io = new Bundle {
val width = in UInt (size bits)
val output = out Bool
}
val ct = Counter(size bits)
ct.increment()
io.output := ct.value < io.width
}
class Apb3PwmCtrl(size: Int) extends Component {
val io = new Bundle {
val apb = slave(
Apb3(
addressWidth = 4,
dataWidth = 32
)
)
val output = out Bool
}
val pwm = new Pwm(size)
io.output := pwm.io.output
val busCtrl = Apb3SlaveFactory(io.apb)
busCtrl.driveAndRead(pwm.io.width, address = 0)
}
今回は、FPGA の入力クロック 16MHz を 8ビットのカウンタで分周しますので、PWM のレートは 16e6 / (1 << 8) = 62.5 kHz になります。ファームウェアによる bit-banging では難しい実装になるかと思います。
あとは、これを Murax SoC の中に APB3 経由で繋ぐだけです。詳しくは、以下の GitHub リポジトリを御覧ください。(ソースコード: src/main/scala/vexriscv/flogics/Murax.scala)
なお、PWM を制御するコードを埋め込んだファームウェアはここにあります。
C言語のプログラムによる制御部分は、こんな感じです。
void main() {
// ...
PWM->WIDTH = 0; // Initialize PWM
// ...
}
void irqCallback() {
// ...
volatile int rx = (UART->DATA) & 0xFF;
UART->DATA = rx;
if (rx >= '0' && rx <= '9')
PWM->WIDTH = (rx - '0') * (255 / 9); // Assign PWM width
}
つまり、UART の受信文字が数字であるとき、その値に応じて PWM の幅(0〜255)を指定している訳ですね。
ようやく、自分が HDL で書いた(小さな小さな)ペリフェラルに標準的なバスを繋ぎ、さらに C 言語で書いたプログラムを FPGA 上の RISC-V で動かすことで、ペリフェラルの制御ができました。SpinalHDL を勉強し始めてから 3ヶ月ということで、ちょっと感激です。でも、頭の柔らかい学生さんや若手技術者なら、もっと簡単に習得できるのではないかと思います。
私は組込ソフト屋なのでソフト設計のほうはなんてことはないのですが、必要に応じて独自のペリフェラルやインターフェイスを設計して動かすことができる、というのは組込設計の応用が広がりそうです。
最後に動かしているところをビデオに納めましたので、御笑納ください。
https://www.youtube.com/watch?v=yb-KG3U9vzU
今日はここまで!
お問い合わせはお気軽に