Forum Discussion
Custom Component Help
Hi all, I'm sorry if this question if too broad, but I'm a beginner to custom components and I need some help. My final goal is to have a custom component in SOPC builder, that performs the SHA-256 hashing algorithm (takes a message in, outputs the hash of the message). I have a VHDL entity that functions as an FSM that computes the SHA hash in 20us (not optimized at all), that I want to make into a custom component. I want the Nios to set the input to the component, wait 20us, and then receive the output.
I have been doing a lot of reading on custom components and the avalon fabric, but I still find it very confusing to define my component in terms of all the signals necessary for avalon master/slave communication. Without any of those, my entity declaration is as follows:entity SHA_Unit is
port(
clk: in std_logic;
rst: in std_logic;
message: in std_logic_vector(255 downto 0);
hash: out std_logic_vector(255 downto 0)
);
end SHA_Unit; Could anyone help me turn this entity into an avalon MM slave peripheral with the nios acting as the master? I can't find any VHDL examples for creating a custom component both in the VHDL and in the C parts. Does anyone know of an example, something as simple as an adder or something? Also, I don't know how to address the fact that my component requires 256 bits of data, while the nios seems only to output up to 32 bits (in pio form anyway). Sorry for the overly general nature of this question, Max15 Replies
- Altera_Forum
Honored Contributor
Hi Max,
One way of getting your 256 bit wide data into/out of your peripheral is with 8 x 32 bit writes/reads. With a 4 bit wide address input to your slave you could latch writes to addresses 0 to 7 into slices of the 256-bit wide register, and have spare addresses for writing control flags/reading status flags. The Avalon signals you'd need to add to your logic would be read, readdata, write, writedata and address. The Avalon interface for this is quite simple. If write is asserted, latch writedata into the destination according to the address bits. If read is asserted, latch data from the relevant source onto readdata. From your C code, use the Altera IORD/IOWR macros to read and write your peripheral using the base address supplied by system.h. You would have to modify your existing hardware module to wait for a control bit to be written before executing the hash, and to set a status flag when the hash is complete. Your C code would then perform the custom function by writing 8 x 32 bit values to the data registers followed by a 'go' bit to the control register, and then polling the status register until the 'ready' bit is set. You can then read the result as 8 x 32-bit values. It would also be easy to use the 'ready' signal as an interrupt. I know this probably isn't as detailed a reply as you'd like, but I hope it at least gives you an idea of how to proceed, and if you have more specific questions feel free to ask. Cheers Sharkybaba - Altera_Forum
Honored Contributor
<div class='quotetop'>QUOTE (sharkybaba @ Jun 25 2009, 03:09 AM) <{post_snapback}> (index.php?act=findpost&pid=22904)</div>
--- Quote Start --- Hi Max, One way of getting your 256 bit wide data into/out of your peripheral is with 8 x 32 bit writes/reads. With a 4 bit wide address input to your slave you could latch writes to addresses 0 to 7 into slices of the 256-bit wide register, and have spare addresses for writing control flags/reading status flags. The Avalon signals you'd need to add to your logic would be read, readdata, write, writedata and address. The Avalon interface for this is quite simple. If write is asserted, latch writedata into the destination according to the address bits. If read is asserted, latch data from the relevant source onto readdata. From your C code, use the Altera IORD/IOWR macros to read and write your peripheral using the base address supplied by system.h. You would have to modify your existing hardware module to wait for a control bit to be written before executing the hash, and to set a status flag when the hash is complete. Your C code would then perform the custom function by writing 8 x 32 bit values to the data registers followed by a 'go' bit to the control register, and then polling the status register until the 'ready' bit is set. You can then read the result as 8 x 32-bit values. It would also be easy to use the 'ready' signal as an interrupt. I know this probably isn't as detailed a reply as you'd like, but I hope it at least gives you an idea of how to proceed, and if you have more specific questions feel free to ask. Cheers Sharkybaba[/b] --- Quote End --- Thank you for your reply! Ok, I changed the VHDL to reflect the avalon ports, and added the control and status flags. I imported the VHDL file into SOPC builder's new component editor and generated a new component. In my project directory I now have a hw.tcl file. I'm confused now about what I need to do to get the addresses for each of these registers. Other files I'm not sure about are sw.tcl, _regs.h, and system.h. How many of these do I need to make, or if they already exist, where do I find them? Also, what files do I need to have together so that I can re-use this component? Thanks! - Altera_Forum
Honored Contributor
Hi Max,
That didn't take you long! I assume here that you're using the IDE approach to building your software project. If you're not then it'll be of limited help. As far as addresses are concerned, the important one is the base address which is defined in system.h. If you're using the IDE like me, you should find it under your system library tree in e.g Release\system_description. It'll be called something like HASH_BLOCK_BASE. Everything within your logic can then be accessed at an offset from that base address using the IORD/IOWR macros. I usually use the native addressing/register slave approach. I'm sorry but I don't know much about packaging up your component into something you can re-use. It's not something I've had a need to do so far. Maybe there's someone else left in this ghost town that can assist further! If not, it might be worth a visit to the Altera Forum too. Cheers Sharkybaba - Altera_Forum
Honored Contributor
I see the base address in the system.h file, but I don't know what register is at each offset. Where can I find this? Also, even a simpler question, are the registers just the ports of my entity (e.g. read, readdata, write, writedata)?
- Altera_Forum
Honored Contributor
The offsets are determined by how your own HDL code uses the 'address' values passed to it. If your custom component is defined to have 4 address bits then your hardware can respond to reads/writes to 16 different address offsets from your base address. Using the native versions of IORD/IOWR, if your C code does an IORD(HASH_BLOCK_BASE, 5) for example, then the value of 'address' placed on the Avalon inputs to your logic will be 5 (and as far as your HDL is concerned the base address is irrelevant). Only you know what your logic places on the readdata port at a particular address offset, so as such you define the registers.
To try and answer the second part of your question; read, readdata etc are not registers that you can read and write. For example, the following line of code: IOWR(HASH_BLOCK_BASE, 8, 1); causes the Avalon switch fabric to place 8 on the 'address' port, 1 on the 'writedata' port and then strobe the 'write' port. Your HDL has to use these signals to latch the data into a register, or maybe just trigger some action. Your HDL is unaware of its base address; that's just there to tell Avalon which slave port it's talking to. Likewise: IORD(HASH_BLOCK_BASE, 3); causes the Avalon switch fabric to place 3 on the 'address' port, strobe the 'read' port and latch whatever your HDL has placed on the readdata port. It's also important to note that the 'Avalon switch fabric' isn't like a normal shared bus where a device has to tri-state its ouputs when not selected. It seems to behave more like a big multiplexer, so in a lot of cases you don't really need to worry about the 'read' signal if you can be sure that for any value of the address input your readdata output is always ready. For more complex situations you can start implementing wait states, latency etc. I hope this goes some way to clarifying things. Once you cracked the concept, it's a thing of beauty. Good luck - Altera_Forum
Honored Contributor
Got it! Thanks so much for your help!
Last thing, does anyone else know which files need to be packaged together so that the component can be re-used in other designs? - Altera_Forum
Honored Contributor
I found it:
http://www.altera.com/literature/hb/qts/qts_qii54004.pdf (http://www.altera.com/literature/hb/qts/qts_qii54004.pdf) That explains it very well. - Altera_Forum
Honored Contributor
Hello again! So everything was working perfectly, but I ended up needing to change over to 32x1 byte read/writes instead of 8x4 byte read/writes. I changed my C code to reflect this in IOWR/IORD calls, but the results are not promising. SOPC builder gives this warning:
"Warning: cpu_0.data_master/SHA_Unit_0.s0: SHA_Unit_0.s0 does not have byteenables. Narrow (less than 32-bit) writes from cpu_0.data_master will result in spurious writes to SHA_Unit_0.s0". I have changed my read and write signals to be only 8-bits instead of 32-bits. Since those signals are only one byte, I don't understand how to use a byteenable to make the results more accurate. Any thoughts? - Altera_Forum
Honored Contributor
I think I found the answer, and it seems to defeat my purpose. In order to go from a 32-bit data bus to an 8-bit data bus but still accommodate the 32-bit size of the processor, the nios performs 4 r/w operations where it previously performed 1. So the byte enable corresponds to the portion of the 32 bit master bus that is to be written from/read to using the 8-bit slave data bus. Thus, the output is still 32 bits and I'm back where I started. To solve this, I used awkward C code to convert back and forth between 1 byte and 4 byte values. Is this the best way?
- Altera_Forum
Honored Contributor
Just make your readdata and writedata signals 32 bits. It doesn't matter if you are only using bits 0-7. You can then ignore the byteenable and avoid the 3 "extra" non-functional read/writes.