And what about doing multiple wrxxxx in a row? Now you have to do all the ugly flipflop thing many times so they can all avoid stalling? Seems bad to me...
I suppose so, but it would have the UART-like problems that you mentioned. It would be an ugly exception to how the cog wants to work.
Is there opcode room for a SNAPCNT
eg Maybe 9 immediate bits fits
upper 2 bits can 'attach' to each of R,W Hub access, which saves needing an extra code line
Lower 7 gives 1..128 choice in delays
>= 16 change the snap rate, and reset the snap timer. * 'impractical' values < 16 give more control choices, like Enable/reset/RunToSnap
Is hub ram now treated the same way as cog, a "byte" is 32bits and +1 points to the next long?
And then of cource hub-address-robin is long address based?
Pros is that opcode for hub access only needs 18bit for 1MB range
And what about doing multiple wrxxxx in a row? Now you have to do all the ugly flipflop thing many times so they can all avoid stalling? Seems bad to me...
Yes, I think buffering is like the UART I mentioned, as you say, at some stage you DO have to stall.
Hence the alternative of SNAPCNT - no extra buffers needed, and optionally 'attaches' to R/W
Effect is to slow them down.
That solves cycle determinism with random address, but is a little blunt in that it stalls - I think the practical repeat delays here will be ~32 (alllows room for Code+Access, like P1 does now)
That is 2x the P1, but if the SysCLK is 2x P1, maybe that is ok.
And what about doing multiple wrxxxx in a row? Now you have to do all the ugly flipflop thing many times so they can all avoid stalling? Seems bad to me...
I would think it should stall execution if you try performing a RD/WRx instruction again before the previous one had a chance to commit/read data from hub ram. Non-stalling mode would effectively have a single byte/word/long buffer.
Is hub ram now treated the same way as cog, a "byte" is 32bits and +1 points to the next long?
And then of cource hub-address-robin is long address based?
Pros is that opcode for hub access only needs 18bit for 1MB range
I think slots/R/W are always 32b, but any BYTE opcode could invisibly select the 32b,and extract/shift the BYTE from within that - so incrementing BYTE opcodes, would fetch the same 32b 4 times (16 fSys repeat) and then +1 to next 32b,for a 17 fSys repeat.
Write byte would need to RMW - did someone say Write opcodes were slower ?
>but any BYTE opcode could invisibly select the 32b,and extract/shift the BYTE
If 18bits for address is pushing it for a opcode, how can there be room for 2more bits for lower address range?
So reading 4 adjacent bytes will take 64+ cycles, though the first rdbyte actually got 32bits and had all of them (if long aligned)
I say, let it be a 32bit only mcu.
>but any BYTE opcode could invisibly select the 32b,and extract/shift the BYTE
If 18bits for address is pushing it for a opcode, how can there be room for 2more bits for lower address range?
I say, let it be a 32bit only mcu.
Present opcode info says
RDBYTE D,PTRA/PTRB
so I'd guess a 32b PTRA gives your answer ?
That's more logic for the buffers, but I can see how that could help write interleave with code.
Yeah, it does require more logic. The other option is to overwrite the buffer, which obviously is a terrible idea. But yeah, the point is to not stall execution just because there's no good way to time random writes. Snapcnt gets close, but wastes cycles. Either way, I think it's important that we have one of these options at our disposal.
However, Read I cannot see, because it cannot do anything until the value is valid, unless there is some flag checked for Read-Data-Register-Now-Valid, but that's looking clumsy.
non-stalled reads are only useful if you have a sort of pipelined loop going where you can do something like perform a RDx, do some work on some data that you requested earlier, then after at least 16 cycles, you get the more recent data you requested and on and on... Otherwise the option is to work with arrays limited to one page of ram, and sync to that the old fashioned way.
Seems to me that a sort of token ring is virtually built in to this scheme. Since the hub address is [15 bit location][4 bit block] if you set up 15 sequential registers in the cog you could read/write a long from/to every other cog once for every rotation using the block read/write. All you really need is for the cogs to agree on a starting location in the ram block. The 4 bit block address would take care of reading/writing it in the correct ram block for the intended cog. So a cog to cog communication method is included with this plan.
Any cog sequentially or randomly accessing at addresses Adr*16+CogID(0..15) will be totally deterministic and will have 1/16 of hub bandwidth ... If all doing so nothing changes compared to traditional scheme (except hub isolates and reduce to 32K, so at least one cog should perform differently to allow data exchange).
Any cog sequentially or randomly accessing at addresses Adr*8+0..7 will also be totally deterministic with 1/8 of the hub bandwidth
To take advantage of the fast sequential bank/page/hubs reading perhaps a sort of token-ring communication between the hubs
Perhaps some inc/dec instructions/pointers can be provided that allows this kind of math
Nope, determinism is not 'lost' as you seem to believe, - it depends on what your code is doing, & determinism has always depended on user code design, and what your code is doing.
Now, the factors are just a little different.
I can see many ways to code and know exactly how many cycles will be needed, and ways to gain much better than16N, when moving Data to or from the HUB.
(and I'm fairly sure that includes HUB to/from Pins, when Chip replies to my earlier questions )
There are also some simple improvements to Pointer Handling, that can work very well with this scheme, to remove additional code complexity, and give higher bandwidths to FIFO/Buffers.
Chip,
Thanks for posting the Verilog code
I am amazed that Cyclone V is slower, but the LEs are different so guess that's a problem in the mix.
How does OnSemi's RAM compare to the one you and Beau did (silicon space) ?
I ask because I realised that more private cog memory could be added to each cog that would execute at full speed using a simple hubexec style
(see http://forums.parallax.com/showthread.php/155687-A-solution-to-increasing-cog-ram-works-with-any-hub-slot-method )
and wondered if there might be some more space on the die.
Alternately, it might be better to have larger cogs and smaller hub - at first I didn't like it, but it has grown on me and I am not sure of the optimal balance - maybe 256KB hub and 16*16KB cogs ???
I got this new memory scheme implemented in Verilog.
It takes ~9,000 LE's, which is equivalent to ~2 cogs. It's hefty, for sure, but buys a lot of performance.
It's closing timing at ~140MHz, which is pretty decent, and on par with a cog's timing, so far. That means we can likely go 160MHz on the FPGA, but not 200MHz. The silicon will be able to reach 200MHz, though, and maybe higher.
I keep wondering if there is some way to collapse the complexity of this. It all comes down to lots of mux's, which are hard to speed up.
Here is the Verilog code that makes the memory system:
It's looking like a split opcode & more choices (I think someone else mentioned split opcodes ? ) wold be best
RDREQ - issues Read Address - (resets RDGET flags ?)
...
RDGET - Fetches RdReq result, stalls if not ready yet (must be paired with RDREQ)
While splitting the instructions like this might be "safer", a single instruction is more efficient for someone who understands the limitations and requirements of it. It hasn't been uncommon for people to try to reduce the number of instructions used in their code as much as possible in order to get it to fit in the limited cog ram, so that's why I'd hate for a read to require two instructions.
>RDREQ - issues Read Address - (resets RDGET flags ?)
>RDGET - Fetches RdReq result, stalls if not ready yet (must be paired with RDREQ)
RDREQ only need the source address, its opcode could use a 18bit intermediate address
A 18bit movs type instruction is needed to self-modify it as to not having use: movs,shrl,movd
it still allows auto-inc/dec on its own address
RDGET gets 9bit for dest, but with no source there is room for other options like ROL, Mask etc.(replacing word and byte opcodes, as the options can emulate that)
You mean a RD/WR instruction that works similar to how the waitvid does? That is the RD/WR is held/buffered until access to the memory block comes around, but the cog can continue to execute the following instructions unless it is another R/W. A second R/W before the previous one is completed will then halt execution. That would be a very good thing to have.
Chip does say it is possible, but I think it is better to have this be done by the R/W instruction rather than an added wait of some kind. Perhaps a "stall until completed" bit, or perhaps just always allow execution to continue unless a second R/W is encountered before the first one completes. Once the first one completes execution can continue until.........etc.
Exactly, hence my suggestion of a SNAPCNT - I think it needs to be adjustable, for granularity, and 16 does not quite guarantee completion.
You also want a slight variant of WAITCNT, as the idea is to SNAP to some known rate, which is not quite what std WAITCNT does.
It is a little hard to encode into R/W as something needs to set & start this rate timer.
I think SNAPCNT can have 9 immediate bits for opcode support, so perhaps upper 2 bits can 'attach' to each of R,W, giving 1..128, and values < 16 give more control choices.
You mean a RD/WR instruction that works similar to how the waitvid does? That is the RD/WR is held/buffered until access to the memory block comes around, but the cog can continue to execute the following instructions unless it is another R/W. A second R/W before the previous one is completed will then halt execution. That would be a very good thing to have.
It would make contiguous R/W to a single memory block fast and simple. The first R/W is buffered, the cog continues execution and if a second R/W comes before the first one is completed execution is blocked. Sort of an automatic wait for a memory block.
And what about doing multiple wrxxxx in a row? Now you have to do all the ugly flipflop thing many times so they can all avoid stalling? Seems bad to me...
The wait for the read to be completed could be done executing useful instructions and/or a wait. It would be up to the programmer to make sure that there has been enough time for the data to be read. That's why they get the big bucks.
That's more logic for the buffers, but I can see how that could help write interleave with code.
However, Read I cannot see, because it cannot do anything until the value is valid, unless there is some flag checked for Read-Data-Register-Now-Valid, but that's looking clumsy.
Chip does say it is possible, but I think it is better to have this be done by the R/W instruction rather than an added wait of some kind. Perhaps a "stall until completed" bit, or perhaps just always allow execution to continue unless a second R/W is encountered before the first one completes. Once the first one completes execution can continue until.........etc.
It does pretty much what you say - ie has a bit to attach the wait to WR or RD, so those opcodes apply the snap, but you need to define the delay value, so that is why a separate opcode (or config register) is suggested.
The SNAPCNT is quite useful outside of HUB loops, which is another reason to have an opcode for it.
The wait for the read to be completed could be done executing useful instructions and/or a wait. It would be up to the programmer to make sure that there has been enough time for the data to be read. That's why they get the big bucks.
Only the 'gain' you think this (expensive & risky) cycle counting is buying, is not real, as when the data does arrive, some cycles need to be stolen to move into the dest register in COG memory. Better to use two opcodes, as that is very clear what is happening, when, and is safely edit-proof.
Yep, that would be great for writing data to hub in a short loop.
It might not seem obvious at first, but it's also handy for when the loop is longer than 16 clocks. You have a deterministic loop w/hub write/read that takes 20 clocks? No problem!
Comments
Is there opcode room for a SNAPCNT
eg Maybe 9 immediate bits fits
upper 2 bits can 'attach' to each of R,W Hub access, which saves needing an extra code line
Lower 7 gives 1..128 choice in delays
>= 16 change the snap rate, and reset the snap timer. * 'impractical' values < 16 give more control choices, like Enable/reset/RunToSnap
And then of cource hub-address-robin is long address based?
Pros is that opcode for hub access only needs 18bit for 1MB range
Yes, I think buffering is like the UART I mentioned, as you say, at some stage you DO have to stall.
Hence the alternative of SNAPCNT - no extra buffers needed, and optionally 'attaches' to R/W
Effect is to slow them down.
That solves cycle determinism with random address, but is a little blunt in that it stalls - I think the practical repeat delays here will be ~32 (alllows room for Code+Access, like P1 does now)
That is 2x the P1, but if the SysCLK is 2x P1, maybe that is ok.
I would think it should stall execution if you try performing a RD/WRx instruction again before the previous one had a chance to commit/read data from hub ram. Non-stalling mode would effectively have a single byte/word/long buffer.
I think slots/R/W are always 32b, but any BYTE opcode could invisibly select the 32b,and extract/shift the BYTE from within that - so incrementing BYTE opcodes, would fetch the same 32b 4 times (16 fSys repeat) and then +1 to next 32b,for a 17 fSys repeat.
Write byte would need to RMW - did someone say Write opcodes were slower ?
If 18bits for address is pushing it for a opcode, how can there be room for 2more bits for lower address range?
So reading 4 adjacent bytes will take 64+ cycles, though the first rdbyte actually got 32bits and had all of them (if long aligned)
I say, let it be a 32bit only mcu.
Present opcode info says
RDBYTE D,PTRA/PTRB
so I'd guess a 32b PTRA gives your answer ?
non-stalled reads are only useful if you have a sort of pipelined loop going where you can do something like perform a RDx, do some work on some data that you requested earlier, then after at least 16 cycles, you get the more recent data you requested and on and on... Otherwise the option is to work with arrays limited to one page of ram, and sync to that the old fashioned way.
Thanks for posting the Verilog code
I am amazed that Cyclone V is slower, but the LEs are different so guess that's a problem in the mix.
How does OnSemi's RAM compare to the one you and Beau did (silicon space) ?
I ask because I realised that more private cog memory could be added to each cog that would execute at full speed using a simple hubexec style
(see http://forums.parallax.com/showthread.php/155687-A-solution-to-increasing-cog-ram-works-with-any-hub-slot-method )
and wondered if there might be some more space on the die.
Alternately, it might be better to have larger cogs and smaller hub - at first I didn't like it, but it has grown on me and I am not sure of the optimal balance - maybe 256KB hub and 16*16KB cogs ???
See Chip's update
http://forums.parallax.com/showthread.php/155675-New-Hub-Scheme-For-Next-Chip?p=1267735&viewfull=1#post1267735
While splitting the instructions like this might be "safer", a single instruction is more efficient for someone who understands the limitations and requirements of it. It hasn't been uncommon for people to try to reduce the number of instructions used in their code as much as possible in order to get it to fit in the limited cog ram, so that's why I'd hate for a read to require two instructions.
>RDGET - Fetches RdReq result, stalls if not ready yet (must be paired with RDREQ)
RDREQ only need the source address, its opcode could use a 18bit intermediate address
A 18bit movs type instruction is needed to self-modify it as to not having use: movs,shrl,movd
it still allows auto-inc/dec on its own address
RDGET gets 9bit for dest, but with no source there is room for other options like ROL, Mask etc.(replacing word and byte opcodes, as the options can emulate that)
I like it.
[QUOTE=mark
Exactly.
[QUOTE=mark
Ha! This explains why you guys insist on coming up with such complicated proposals - it's all a massive conspiracy to raise programmer's pay rates!
See my expanded description of SNAPCNT here
http://forums.parallax.com/showthread.php/155692-Nibble-Carry-Higher-speed-Buffers-FIFOs-using-new-HUB-Rotate
It does pretty much what you say - ie has a bit to attach the wait to WR or RD, so those opcodes apply the snap, but you need to define the delay value, so that is why a separate opcode (or config register) is suggested.
The SNAPCNT is quite useful outside of HUB loops, which is another reason to have an opcode for it.
Only the 'gain' you think this (expensive & risky) cycle counting is buying, is not real, as when the data does arrive, some cycles need to be stolen to move into the dest register in COG memory.
Better to use two opcodes, as that is very clear what is happening, when, and is safely edit-proof.
It might not seem obvious at first, but it's also handy for when the loop is longer than 16 clocks. You have a deterministic loop w/hub write/read that takes 20 clocks? No problem!