New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revisiting ClockSource annotations... #145
Comments
Thanks for bringing this up. This will help along the discussion in clash-lang/clash-prelude#36, where the the use of explicit clock lines would help build PLL components like this. |
@christiaanb Which Wiki page? |
@mgajda Ah, sorry, I forgot to link it. It's this wiki page: https://github.com/clash-lang/clash-prelude/wiki/Clock-and-reset-modelling |
Hi, I also ran into this while trying to get CLaSH working on the iCE40-HX8K breakout board, as I wanted a fully synthesize-able result without having to hack the reset wire with a wrapper or anything like that. As an easy workaround for anyone else who stumbles here, it's relatively simple to get by with fixed clock frequencies for the PLL component of the board. Let's say you want to tune the iCE40 PLL to be a 60mhz clock. You can ask
Next, simple verilog wrapper: module lattice_ice40_60mhz(input wire clk, output wire clkout, output wire locked);
SB_PLL40_CORE #(
.FEEDBACK_PATH("SIMPLE"),
.PLLOUT_SELECT("GENCLK"),
.DIVR(4'b0000),
.DIVF(7'b1001111),
.DIVQ(3'b100),
.FILTER_RANGE(3'b001)
) uut (
.LOCK(locked),
.RESETB(1'b1),
.BYPASS(1'b0),
.REFERENCECLK(clk),
.PLLOUTCORE(clkout)
);
endmodule Define a 60mhz module CLaSH.Lattice.ICE40
( pll60mhz
) where
import CLaSH.Prelude
import CLaSH.Signal.Explicit (systemClock)
pll60mhz :: String -> ClockSource
pll60mhz clkExpr = ClockSource
{ c_name = "lattice_ice40_60mhz"
, c_inp = pure ("clk", clkExpr)
, c_outp = [("clkout", show systemClock)]
, c_reset = Nothing
, c_lock = "locked"
, c_sync = False
} Note: I did not hook up the reset wire in my example as it's not needed, but considering the simplicity of defining this And that seemed to do the trick. Here's an example of a muxed blinker, with constant input signals, that will blink LED0 on the Breakout board at a 10Hz interval using the PLL at 60 megahertz (I wouldn't try the 50 or 100hz options since they'll blink too fast for your human vision to register it, but the 1Hz and 10Hz signals work fine). This is based on the Verilog tutorial example available at Nandland: module LED1 where
import CLaSH.Prelude
import CLaSH.Lattice.ICE40
import Data.Bits
import Data.Bool
import Data.Bifunctor
type Enable = Signal Bit
type Switch = Signal Bit
--------------------------------------------------------------------------------
-- Speed utilities
data Speed
= Speed_1HZ
| Speed_10HZ
| Speed_50HZ
| Speed_100HZ
khz = (* 1000)
mhz = (* (1000*1000))
duty x = (x `div` 2) - 1
tick :: Speed -> Integer
tick Speed_1HZ = duty (mhz 60 `div` 1)
tick Speed_10HZ = duty (mhz 60 `div` 10)
tick Speed_50HZ = duty (mhz 60 `div` 50)
tick Speed_100HZ = duty (mhz 60 `div` 100)
--------------------------------------------------------------------------------
-- Circuit definition
flipper :: Unsigned 32 -> Signal Bit
flipper lim = mealy fsm (low, 0) $ signal ()
where
fsm (s,i) () | i == lim = ((complement s, 0), complement s)
| otherwise = ((s, i+1), s)
toggle :: Speed -> Signal Bit
toggle spd = case spd of
Speed_1HZ -> flipper 29999999 -- duty (mhz 60 `div` 1)
Speed_10HZ -> flipper 2999999 -- duty (mhz 60 `div` 10)
Speed_50HZ -> flipper 599999 -- duty (mhz 60 `div` 50)
Speed_100HZ -> flipper 299999 -- duty (mhz 60 `div` 100)
blinker :: Enable -> Switch -> Switch -> Signal Bit
blinker e i1 i2 = speed .&. e where
c0 = (i1 .==. 1) .&&. (i2 .==. 1)
c1 = (i1 .==. 1) .&&. (i2 .==. 0)
c2 = (i1 .==. 0) .&&. (i2 .==. 1)
speed = mux c0 (toggle Speed_1HZ)
$ mux c1 (toggle Speed_10HZ)
$ mux c2 (toggle Speed_50HZ)
$ toggle Speed_100HZ
--------------------------------------------------------------------------------
-- CLaSH exports: top level entity, and test bench
topEntity :: Signal Bit
topEntity = blinker (pure 1) (pure 1) (pure 0)
{-# ANN topEntity
(defTop
{ t_name = "led1"
, t_inputs = []
, t_extraIn = [ ("clk", 1) ]
, t_outputs = ["LED0"]
, t_clocks = [ pll60mhz "clk" ]
}) #-} And a simple build script: #!/usr/bin/env bash
set -x
if [ "x$1" == "x--clean" ]; then
rm -rf verilog *.blif *.log *.v obj *.asc *.bin
exit 0
fi
rm -rf verilog *.blif *.log *.v obj *.asc *.bin
clash -hidir obj -odir obj --verilog LED1.hs
{
FILES=`find verilog/ -type f -iname '*.v' | grep -v test`
FILES+=" "
FILES+=`find CLaSH/ -type f -iname '*.v'`
for f in $FILES; do
echo "read_verilog -Iverilog/LED1 $f"
done
echo "synth_ice40 -top led1 -blif led1.blif"
echo "write_verilog -attr2comment led1.v"
} > synth.ys
yosys -v3 -l synth.log synth.ys; rm -rf synth.ys obj
arachne-pnr -d 8k -o led1.asc -p pins.pcf led1.blif
icepack led1.asc led1.bin
icetime -tmd hx8k led1.asc You can upload that with Boom! A fully open source High Level Synthesis toolchain + a fully open source RTL synthesis flow. Not bad. You could also probably parameterize the If it were somehow possible to inline the verilog shim around |
@thoughtpolice Thanks for this thorough description on how to get the I've started on making it possible to specify clock sources inside clash at https://github.com/clash-lang/clash-prelude/blob/explicit_clock_reset/src/CLaSH/TopEntity.hs So that is going to get us part of the way of packaging up components on hackage, now I just need to figure out how to associate a Haskell function with a Verilog/VHDL template. I can probably do that latter part with |
Primitive definitions can now included in a package to be uploaded to hackage by: 82cd318 |
Now that clock and reset lines are explicit, clock source annotations are removed, and clock manipulating blocks like PLLs can be instantiated in the Haskell code and implemented using HDL templates. |
Use a `reflection`-like API for implicit clocks and resets
I have recently played with fully open source FPGA toolchain targeting Lattice iCE40 devices.
The devices are cheap, and compile-upload cycle takes about 5 seconds on my laptop, so there is a lot to gain there.
However, I noticed that the current
ClockSource
is incompatible with the two clock setups that I wanted to try on this device.system1000
source, and assertsystem1000_rst
to be permanently1
. I currently see no way to configure it fully within CLaSH source, since there is no way to accesssystem1000_rst
(or force a bit of Verilog to the output.)Unless we find a pin that is permanently asserted, or add our own Verilog wrapper for
topEntity
.DIVR
,DIVF
, andDIVQ
. That would even allow for dynamic clock scaling, if one is careful enough to avoid metastability when configuration is changed. Below is example code from Clifford, that configures Lattice clock. All it needs is an ability to give extra inputs to theClockSource
:PS Code comes from Reddit
The text was updated successfully, but these errors were encountered: