-
Notifications
You must be signed in to change notification settings - Fork 155
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
SPI: Generalize to multi-lane MOSI/MISO #2635
base: master
Are you sure you want to change the base?
Conversation
120341d
to
d9b0c33
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.
Ah, thank you for your contribution! This looks good to me, I just have some minor points.
I know we have named functions f'
in the past when we made a variant of f
. But I'm not a big fan of the prime and I as well as at least some of my colleagues are moving away from it. So I'd like to suggest to call the new functions spiMasterWide
and spiSlaveWide
instead.
1e8eca6
to
f161a5a
Compare
Thanks for the review, @DigitalBrains1. I have addressed your feedback. |
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.
Hehe, this is a bit silly, but...
So, the functions spiCommon
, spiMaster
, spiMasterWide
and spiGen
take an argument of type SPIMode
. It makes sense to call this argument mode
. Conversely, the functions spiSlave
and spiSlaveWide
take an argument of type SPISlaveConfig
. It makes sense to call this argument cfg
(if it needs to be bound to a name at all).
Your first version of this PR had that with one exception: spiMaster
's argument name got changed from mode
to cfg
, and I asked whether you could rename it back to mode
.
You did that in your second version, but you also changed the name for the argument to spiSlave
to mode
, but that should just have stayed the cfg
you initially made it.
Just to actually have stuff make sense, could you name that argument to spiSlave
to cfg
again, after that I'll merge it! :-D
^ Open for discussion of course, but given the fact that |
I don't have a strong opinion on this. |
Although if we're going to change API's, I also want to change the type of the (added overline to emphasise the active-low character) |
Sigh, yes. Sorry about that, @DigitalBrains1. I will fix this now.
I also don't have a strong opinion here. I see little harm having the simpler wrappers available for the common, single-lane case.
Indeed, I think changing the type here would be a very good idea. |
f161a5a
to
2721bc3
Compare
Also, I do wonder whether packaging the SPI signals into a record might improve usability by eliminating repetition in the types. |
Yes, let's make it record types. Thanks for collaborating on this! While you're in there, could you also rename this , spiSlaveConfigLatch :: Bool
-- ^ Whether to latch the SPI pins
--
-- Recommended:
--
-- * Set to /True/ when core clock is /more/ than twice as fast as the SCK
-- Clock: 2*SCK < Core
--
-- * Set to /False/ when core clock is twice as fast, or as fast, as the SCK to reflect the fact that this option inserts a flip-flop, not a latch... we don't even have latches in Clash. Within the Clash ecosystem, we use the terminology in a way such that a latch and a flip-flop are two distinct things (I'm aware in general there is ambiguity). Perhaps rename the record That is the only use of the word latch that affects the API. Other occurences should probably also be corrected but don't affect the API, so we can do that at a later time. With record types, I don't think there's much call for a convenience function for single-lane applications. I'd imagine there'll be a type |
(By the way, I'm concerned that the sampling of SCK is prone to metastability. Even with the (Also, Set to /False/ when core clock is twice as fast, or as fast, as the SCK, whut? SCK is sampled as data, surely it is absolutely impossible to sample when SCK isn't at least twice as slow as the core clock) |
|
My pleasure! The next question is precisely how to structure these types. The obvious encoding would be: data Spi_M2S f = Spi_M2S { spiSclk, spiCs, spiMosi :: Bit }
data Spi_S2M f = Spi_S2M { spiMiso :: Bit } However, in my own projects I generally use higher-rank types to represent such products: data Spi_M2S f = Spi_M2S { spiSclk, spiCs, spiMosi :: f Bit }
data Spi_S2M f = Spi_S2M { spiMiso :: f Bit } This gives a pleasing symmetry between bundled and unbundled representations (although admittedly the bundled representation is a bit less convenient to use). However, it may not be worthwhile in this case as the user generally shouldn't need to work with this type in unbundled form. |
I agree that in this particular instance, dealing with the unbundled form is not really something you'd do. |
I was thinking along these lines data SpiSlaveIn (ds :: BiSignalDefault) (dom :: Domain) (numLanes :: Nat) =
SpiSlaveIn {
spiSlaveMosi :: "MOSI" ::: Signal dom (BitVector numLanes),
spiSlaveMisoIn :: "MISO" ::: BiSignalIn ds dom numLanes,
spiSlaveSck :: "SCK" ::: Signal dom Bit,
spiSlaveSs :: "SS" ::: Signal dom Bit
}
data SpiSlaveOut (ds :: BiSignalDefault) (dom :: Domain) (numLanes :: Nat) =
SpiSlaveOut {
spiSlaveMisoOut :: "MISO" ::: BiSignalOut ds dom numLanes
} and similar for [edit] |
Don't forget to add a copyright line at the top of the file. |
Hmmmm, I think we don't support Also, I'm pretty sure However, the question is what to do about this. Are you just going to use the SPI master? I'm leaning towards just documenting the deficiencies of the slave for now. In an ideal world we'd be able to work on everything, in the real world we have to prioritise. |
Yes, my interests lie only with the master. |
As far as I'm concerned, if you want to, we can convert just the |
Seems to me that if we can't support records for all functions, we should either:
What are the authors's / reviewers's preferences? |
First of all, I'm fine with accepting it as-is, as I've indicated before. I'm also not against your proposal point 1, but I don't think it needs to be part of this PR. @bgamari is only interested in the master, so we can do the work on the slave ourselves after this PR is merged. Note that only the slave needs Finally, diverging from your proposal point 1, I'm also fine with this follow-up work not being done in the short term. I think the SPI master is going to see much more use than the slave. I also think the slave needs to be improved to be generally useful. The mode without a register on the SCK line is just prone to metastability, in my opinion it has no right to exist. I'm pretty sure I can construct a really basic metastability scenario where it already misses SCK clock edges. The mode that registers the input with a single register is pretty resilient; the special flip-flops on input pins make it difficult to drive them into metastability. But still, I'd feel even better with a proper DFF synchroniser. I don't know how important a DFF synchroniser is. We'd have to think about and document maximum clock rate ratios for this to work; the slave will drive its own data out later when its SCK input is delayed by the synchroniser. This means the setup time for the MISO input in the master is reduced, and the maximum clock rate is reduced accordingly. But on the flip side, we can reduce the response time by dropping the output register in the slave on MISO; since this is a synchronous protocol, I don't think there is any downside to MISO glitching for a bit other than power draw and EMI emissions. Those might be relevant, so it should probably be optional, for the people who want that extra oomph of interface speed. [edit] We could also consider a slave where the SCK is routed directly to the flip-flop clock inputs if people want the highest clock rates possible. It would make configuring the SPI mode more difficult (specifically, which clock edge is the sample clock edge).[/edit] The whole ¹ Please use |
2721bc3
to
aaf2626
Compare
@DigitalBrains1, I have rebased this and (I believe) addressed your comments. Thanks again for your help and reviews. |
aaf2626
to
2e2f332
Compare
It is fairly common for single SPI bus to consist of a set of parallel MISO/MOSI lanes (c.f. QSPI FLASH). For instance: * many multi-channel ADCs allow each converter to clock out over its own MISO lane to reduce the clockrate needed to achieve the designed conversion rate. * similarly, QSPI FLASH relies upon four bidirectional outputs to increase data rate. Here we extend Clash.Cores.SPI to facilitate this use-case by introducing `spiMaster'` and `spiSlave'`, which allow arbitrary MISO/MOSI lane widths.
2e2f332
to
c8e8826
Compare
Thanks for your further work! I will post more review later, but I started with a couple of proposed changes which I have put in another branch. First of all, we have the issue that you cannot make a That's why I suggest you leave the SPI slave completely alone. We'll deal with it later. Therefore, I have created an alternative branch where only the master (and Secondly, I'd like to make the change of So I added a commit in my alternative branch which changes S̅S̅ and SCK to be My suggested branch is at peter/multilane-spi. It is supposed to be identical to your branch except for the two points above. [edit] |
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.
Don't forget to add yourself to the copyright line at the top! In fact, I forgot that as well, so I pushed a fixup!
commit to my branch.
I think you already read it in the functional programming Slack channel, but we plan to update the terminology we use for SPI to the OSHWA suggested terminology: Controller, Peripheral, PICO, POCI, SCK (unchanged), CS. This will be in some future PR, not right here. Just like we are changing the terminology for our I²C stuff to the new NXP terminology.
type instance TryDomain t (SpiSlaveOut ds dom misoLanes mosiLanes) = 'Found dom | ||
|
||
-- | Output signals of an SPI master. | ||
data SpiMasterOut (dom :: Domain) (misoLanes :: Nat) (mosiLanes :: Nat) = |
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 suppose you want to keep the type-level arguments all the same across these types, but I think it's better to just declare arguments that are actually used. So SpiMasterOut
doesn't need misoLanes
and SpiMasterIn
doesn't need mosiLanes
.
clash-cores/src/Clash/Cores/SPI.hs
Outdated
, Signal dom Bit -- MOSI | ||
, Signal dom Bool -- SS | ||
-> SpiMasterIn dom 1 1 | ||
-> ( SpiMasterOut dom 1 1 |
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.
Could you update the Haddock for the output of this function?
type instance TryDomain t (SpiSlaveOut ds dom misoLanes mosiLanes) = 'Found dom | ||
|
||
-- | Output signals of an SPI master. | ||
data SpiMasterOut (dom :: Domain) (misoLanes :: Nat) (mosiLanes :: Nat) = |
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.
In the functions, you use the names mosiW
and misoW
, but here you use mosiLanes
and misoLanes
to refer to the same quantity. Could you pick one and apply that name everywhere?
-- | Output signals of an SPI master. | ||
data SpiMasterOut (dom :: Domain) (misoLanes :: Nat) (mosiLanes :: Nat) = | ||
SpiMasterOut { | ||
spiMasterMosi :: "MOSI" ::: Signal dom (BitVector mosiLanes), |
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 the data inputs and outputs of spiMaster
are of type Vec mosiW (BitVector n)
and Vec misoW (BitVector n)
respectively, but MOSI
is defined as BitVector mosiLanes
and MISO
as BitVector misoLanes
.
The first thing that I think when I see this is: wait, a Vec
is numbered with ascending inices (!!
) and a BitVector
is numbered with descending indices (!
)! So... how do those indices map?
And even if they were the same, I'd still wonder about the mapping to HDL indices. Does din !! 0
map to MOSI[0]
or MOSI[n]
in my SDC file?
However, I think BitVector _
for MOSI/MISO does have an advantage: it doesn't give the annoying
[...]: warning:
Make sure HDL port names are correct:
Backtracked when constructing Clash.Sized.Vector.Vec
(Type appears recursive)
I'm not really sure about the type. We should clearly document the mapping of Vec
indices to pins on the top entity, and ideally they'd match, so din !! 0
does map to MOSI[0]
. But whether MOSI
should be BitVector
or Vec _ Bit
I'm not so sure about.
-- ^ Data to send from master to slave, transmission starts when receiving | ||
-- /Just/ a value | ||
-> SpiMasterIn dom 1 1 | ||
-> ( SpiMasterOut dom 1 1 |
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.
Could you add Haddock for the output tuple?
It is fairly common for single SPI bus to consist of a set of parallel MISO/MOSI lanes (c.f. QSPI FLASH). For instance:
many multi-channel ADCs allow each converter to clock out over its own MISO lane to reduce the clockrate needed to achieve the designed conversion rate.
similarly, QSPI FLASH relies upon four bidirectional outputs to increase data rate.
Here we extend Clash.Cores.SPI to facilitate this use-case by introducing
spiMaster'
andspiSlave'
, which allow arbitrary MISO/MOSI lane widths.Still TODO:
Write a changelog entry (see changelog/README.md)Not a public package, so no changelog needed yet