-
Notifications
You must be signed in to change notification settings - Fork 159
Move i2c to clash cores #2584
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
base: master
Are you sure you want to change the base?
Move i2c to clash cores #2584
Conversation
e1989c6
to
8553ddc
Compare
clash-cores/test/Test/Cores/I2C.hs
Outdated
|
||
{-# ANN system Synthesize { t_name = "system", t_inputs = [], t_output = PortName "" } #-} | ||
system = system0 systemClockGen resetGen | ||
{-# ANN testBench Synthesize { t_name = "testBench", t_inputs = [], t_output = PortName "" } #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed, some comments:
testBench
is a magic name; I think it's better to give it a different name. TheSynthesize
annotation probably masks the special machinery.- The record syntax with a space between the constructor and the
{
is confusing, it looks like function application. How many arguments doesANN
take? :-) - We didn't document this yet, but we did discuss steering people away from the constructor and to using
defSyn
instead. I think mainly so we can add fields without existing use sites suddenly having undefined field contents.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah forget about the defSyn
that doesn't work... <sad trombone>
fb61cae
to
2efc4a0
Compare
bde9655
to
f08ba82
Compare
6af2276
to
d663a1a
Compare
bfa702d
to
e9b217b
Compare
0ccc4ac
to
41776f8
Compare
Also remove `Synthesize` annotations from `i2c` core and internals.
…binatorial Co-authored-by: lucas@qbaylogic.com
Hide the print statements of the I2C unittest behind debug flags. This debug flag is currently always False. TODO: Expose the debug flag of the I2C unittest
Since the I2C core is subject to changes in the near future, we've moved it to an `Experimental` submodule.
41776f8
to
59cb3c7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from commit messages stating that specific changes were made, they should explain why the changes were made.
@@ -158,24 +158,6 @@ runClashTest = defaultMain $ clashTestRoot | |||
, clashTestGroup "crc32" | |||
[ runTest "CRC32" def | |||
] | |||
, clashTestGroup "i2c" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The I2C test is:
- The only test in the clash testsuite that has multiple synthesize annotations
- Tests
SimIO
in a common user scenario.
So I would like it if this test wasn't removed. And given that the I2C core in clash cores has a new API, perhaps we can either just:
- Keep the old domain-monomorphic code in the
examples
directory - Or move the old domain-monomorphic code to a test directory
So that we still have a test with multiple Synthesize annotations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Sounds like the best option to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also agree with 2, we can just put the old code in a test directory.
@@ -29,10 +29,8 @@ data ByteMasterS | |||
, _coreTxd :: Bit -- coreTxd register | |||
, _shiftsr :: Bool -- shift sr | |||
, _ld :: Bool -- load values in to sr | |||
, _i2cOpAck :: Bool -- host cmd acknowlegde register |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the command ack made combinational? The commit message doesn't state why this change was made.
-> do display "CONFaddrAck" | ||
pure s { i2cConfStateM = CONFreg | ||
, i2cWrite = False | ||
-> if rxAck then do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these changes made because the ack made combinational in the another commit? Perhaps mention that in the commit message then.
@@ -22,8 +22,18 @@ data ConfS = ConfS { i2cConfStateM :: ConfStateMachine | |||
, i2cConfFault :: Bool | |||
} | |||
|
|||
type ConfI = (Bool,Bool,Bool,Bool,Bool) | |||
type ConfO = (Bool,Maybe I2COperation,Bool,Bool) | |||
data ConfI = ConfI { i2cRst :: Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit message just says "refactor", perhaps explain what the refactor does.
@@ -49,7 +52,7 @@ configT | |||
-> SimIO ConfO | |||
configT s0 ConfI{i2cRst=rst,i2cEna=ena,i2cCmdAck=cmdAck,i2cRxAck=rxAck,i2cAl=al} = do | |||
s <- readReg s0 | |||
let ConfS confStateM claim op lutIndex fault = s | |||
let ConfS confStateM claim op lutIndex fault debug = s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are debug messages made conditional? Perhaps that could be mentioned in the commit message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because otherwise they flood the terminal when you run clash-cores:unittests
:)
-- | Core for I2C communication. Returns the output enable signals for SCL en SDA | ||
-- These signals assume that when they are `high`, they pull down SCL and SDA respectively. | ||
-- For 2-wire I2C, you can use BiSignals (`Clash.Signal.Bidirectional.BiSignalIn` and `Clash.Signal.Bidirectional.BiSignalOut`) | ||
-- | ||
-- === __Example__ | ||
-- | ||
-- @ | ||
-- i2cComp clk rst ena sclIn sdaIn = (sclOut, sdaOut) | ||
-- where | ||
-- sclOut = writeToBiSignal sclIn (mux sclOe (pure $ Just 0) (pure Nothing)) | ||
-- sdaOut = writeToBiSignal sdaIn (mux sdaOe (pure $ Just 0) (pure Nothing)) | ||
-- (sclOe, sdaOe) = unbundle i2cO | ||
-- i2cIn = bundle (readFromBiSignal sclIn, readFromBiSignal sdaIn) | ||
-- (dout,i2cOpAck,busy,al,ackWrite,i2cOut) = i2c clk arst rst ena clkCnt claimBus i2cOp ackRead i2cI | ||
-- @ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The outputs for SCL and SDA are Maybe Bit
, neither the haddock comment, nor the code example seem to get this rights.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I did not have a chance to actually look at this code and the changes; and I don't have time anymore to do so now. So I'm making a bunch of assumptions and next week I'll see if I was off base)
SCL and SDA are open-drain I/O pins: they are both input and output, and they are either driven low or high-impedance. I assume the Maybe Bit
therefore has two states: Nothing
(Hi-Z) and Just 0
(drive low). And I suppose this encoding was chosen because in an FPGA, that's how you would drive the tri-state used to implement an open-drain pin on the FPGA. Specifically, the trivial way to implement open-drain with a tri-state is to have the input from the FPGA tied to ground and put your data on the gate: gated when high, outputting when low.
So this Maybe Bit
is purely pragmatic: it encodes how you drive a tristate, but it only has two states. It's also not entirely practical; we have two tri-state primitives, one for the ICE40 and one for the ECP5, and one of those uses an enable signal to gate, and the other a Signal Bool
to gate; neither actually uses Maybe
to gate.
So I'd like to propose we change the SCL and SDA outputs to just be Bit
. And this would then encode the two possible states: 1 = Hi-Z, 0 = drive low, just like I²C defines it afaik.
And related but not for this PR, I'd like to harmonise our two tri-state primitives, and define wrappers that can be used to implement open-drain and open-source, where the data inputs to those wrappers are just Bit
and they wrap the underlying tri-state primitives in the logical way (data input tied to rail, gate input tied to data, either inverted or direct). But that's for later; it just clarifies how I see the future for using this I²C core with I/O buffer primitives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this Maybe Bit is purely pragmatic: it encodes how you drive a tristate, but it only has two states
Indeed! It's to show that it drives a tristate.
It's also not entirely practical; we have two tri-state primitives, one for the ICE40 and one for the ECP5, and one of those uses an enable signal to gate, and the other a Signal Bool to gate; neither actually uses Maybe to gate.
I have never seen these primitives to be honest, the choice was made to be compatible with BiSignals. at least Vivado simply infers the tristate buffer when you use BiSignal. We don't use any explicit primitives in the Bittide implementations.
Personally I prefer the Maybe Bit
over `Bit.
And related but not for this PR, I'd like to harmonise our two tri-state primitives, and define wrappers that can be used to implement open-drain and open-source, where the data inputs to those wrappers are just Bit and they wrap the underlying tri-state primitives in the logical way (data input tied to rail, gate input tied to data, either inverted or direct). But that's for later; it just clarifies how I see the future for using this I²C core with I/O buffer primitives.
Personally I don't find it intuitive to make the behavior depend on the outside world, whats the downside of using Maybe Bit
to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Bit
can do the wrong thing. You could output Just 1
which is wrong for i2c. I would like to propose a third option. Have a unit data type newtype Low = Low
or something like that and output Maybe Low
. If you want you can even make a custom BitPack
so that it still implemented as 2 bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed the core should never output Just 1
, and it never does. I don't see a problem in using Maybe Bit
as tristrate drive type for i2c. It intuitively hints to the user that its mean to be a tristate driver
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at least Vivado simply infers the tristate buffer when you use BiSignal.
Oh that's good to know! I just immediately thought of those two primitives.
Thinking about it a bit more, a wrapper for a BiSignal
is less pretty in API than I initially imagined it could be. But still I'm leaning towards it being the right way. It would also hide a bit of the complexity of BiSignal
for this use.
We could add this to the Prelude:
openDrainIO :: BiSignalIn ds dom (BitSize a) -> Signal dom a -> (BiSignalOut ds dom (BitSize a), Signal dom a)
openSourceIO :: BiSignalIn ds dom (BitSize a) -> Signal dom a -> (BiSignalOut ds dom (BitSize a), Signal dom a)
and people wouldn't have to do all the readFromBiSignal
and writeToBiSignal
themselves.
Personally I don't find it intuitive to make the behavior depend on the outside world, whats the downside of using
Maybe Bit
to you?
It's semantically wrong. It is a two-valued signal, not a three-valued one. If you want to force people towards a specific implementation, you should perhaps create a new type OpenDrain
or something like that. So what's above becomes:
openDrainIO :: BiSignalIn ds dom (BitSize a) -> Signal dom (OpenDrain a) -> (BiSignalOut ds dom (BitSize a), Signal dom a)
(that's my counter-suggestion to Rowan's Low
. I think it is more obvious in its intention.)
It intuitively hints to the user that its mean to be a tristate driver
In an FPGA, that is the usual implementation, yes, since it's there anyway. But it's not supposed to be a tri-state driver in my view. It's supposed to be an open-drain driver. But in an FPGA, every I/O pin is connected to a tri-state driver, and that's what you use. A standard totem-pole output is a tri-state that is never Hi-Z, an open-drain output is a tri-state that is never driven high, and an open-source output is a tri-state that is never driven low. Three types of outputs, all of them having two states. Only the true tri-state output has three states.
In an ASIC, you would never use a tri-state to implement an open drain, it would be wastage of die space on a staggering scale, relatively speaking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't intend to codify the BiSignal
in the I²C stuff; people are free to choose their pin driver. But I think you either use platform-specific primitives or the BiSignal
stuff. As I said, I don't mean to include these Prelude extensions as part of this PR, it's just how I envision it being used, which supports my argument in favour of just Bit
.
Currently the i2c core in the
examples
folder can not be used without copying the files or having a local clone of the reposity. This pull request moves the core over toclash-cores
and aims to making it properly documented.TODO:
There are a few things which I'd like added to this PR:
I've created an issue for the changes which I think are needed before moving the I2C core out of its Experimental submodule.