Nios® V/II Embedded Design Suite (EDS)
Support for Embedded Development Tools, Processors (SoCs and Nios® V/II processor), Embedded Development Suites (EDSs), Boot and Configuration, Operating Systems, C and C++

Custom Component Help

Altera_Forum
Honored Contributor II
1,478 Views

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, 

Max
0 Kudos
15 Replies
Altera_Forum
Honored Contributor II
643 Views

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
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

<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&#39;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 &#39;go&#39; bit to the control register, and then polling the status register until the &#39;ready&#39; bit is set. You can then read the result as 8 x 32-bit values. 

 

It would also be easy to use the &#39;ready&#39; signal as an interrupt. 

 

I know this probably isn&#39;t as detailed a reply as you&#39;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&#39;s new component editor and generated a new component. In my project directory I now have a hw.tcl file. I&#39;m confused now about what I need to do to get the addresses for each of these registers. Other files I&#39;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!
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

Hi Max, 

 

That didn&#39;t take you long! 

 

I assume here that you&#39;re using the IDE approach to building your software project. If you&#39;re not then it&#39;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&#39;re using the IDE like me, you should find it under your system library tree in e.g Release\system_description. It&#39;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&#39;m sorry but I don&#39;t know much about packaging up your component into something you can re-use. It&#39;s not something I&#39;ve had a need to do so far. 

 

Maybe there&#39;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
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

I see the base address in the system.h file, but I don&#39;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)?

0 Kudos
Altera_Forum
Honored Contributor II
643 Views

The offsets are determined by how your own HDL code uses the &#39;address&#39; 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 &#39;address&#39; 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 &#39;address&#39; port, 1 on the &#39;writedata&#39; port and then strobe the &#39;write&#39; 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&#39;s just there to tell Avalon which slave port it&#39;s talking to.  

 

Likewise: 

 

IORD(HASH_BLOCK_BASE, 3); 

 

causes the Avalon switch fabric to place 3 on the &#39;address&#39; port, strobe the &#39;read&#39; port and latch whatever your HDL has placed on the readdata port. 

 

It&#39;s also important to note that the &#39;Avalon switch fabric&#39; isn&#39;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&#39;t really need to worry about the &#39;read&#39; 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&#39;s a thing of beauty. 

 

Good luck 

 

 

0 Kudos
Altera_Forum
Honored Contributor II
643 Views

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?
0 Kudos
Altera_Forum
Honored Contributor II
643 Views
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

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&#39;t understand how to use a byteenable to make the results more accurate. 

 

Any thoughts?
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

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&#39;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?

0 Kudos
Altera_Forum
Honored Contributor II
643 Views

Just make your readdata and writedata signals 32 bits. It doesn&#39;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.

0 Kudos
Altera_Forum
Honored Contributor II
643 Views

<div class='quotetop'>QUOTE </div> 

--- Quote Start ---  

Just make your readdata and writedata signals 32 bits. It doesn&#39;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.[/b] 

--- Quote End ---  

 

 

That makes sense on the hardware side, but in the Nios IDE I don&#39;t see how it would work. When I call the IOWR macro for example, doesn&#39;t the input data need to match up to the bit width of the component data bus? How would I use the IOWR/IORD functions to use only the lowest byte of the 4 byte value?
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

If you have defined your custom component as a register slave it will use native addressing. That means that if your slave is only 8 bits wide it should ignore the upper 24 bits of the data written by IOWR. Likewise, IORD will return your slave data in the lower 8 bits of a 32 bit word, with the upper 24 bits set to zero. 

 

For the issue of byte enables to arise, I&#39;d guess you must have defined your component as a memory slave so it will use dynamic addressing. To read/write a single byte, you&#39;d have to use IOWR_8DIRECT and IORD_8DIRECT. Have a look in the file io.h if you want to understand what these macros do. Also, the Avalon spec. document explains the difference between native and dynamic addressing and how different data bus widths are handled. 

 

 

 

0 Kudos
Altera_Forum
Honored Contributor II
643 Views

You wrote:  

"That makes sense on the hardware side, but in the Nios IDE I don&#39;t see how it would work. When I call the IOWR macro for example, doesn&#39;t the input data need to match up to the bit width of the component data bus? How would I use the IOWR/IORD functions to use only the lowest byte of the 4 byte value? " 

 

It is not a matter of figuring out how to restrict IOWR/IORD to manage the lowest byte of the 4 byte value. It always passes and receives 32 bits. It is your custom logic that manages using only the lowest byte of the 4 byte values by utilizing only writedata[7:0], and setting readdata[7:0] bits only. If you desire, you can explicitly set readdata[31:8] to zeros in your module, but it probably is not necessary. 

 

Perhaps you don&#39;t like the inconsistency of passing a char/byte variable to the IOWR macro, which in turn writes to a 32-bit port. If that is the case, cast your variable to an unsigned int which will be 32 bits wide with the low order 8 bits equal to your variable. I haven&#39;t looked at it, but the macro may already do this. The alternative is to assign a temp 32-bit unsigned int variable equal to your char/byte variable, and passing the temp variable to IOWR and using IORD with your temp variable and assign your 8-bit variable to (temp_var && 0xFF), or something like eight_bit_var = (unsigned char) (IORD(base,offset) & 0xff); 

 

I suggest looking at the macro, or the dissassembly of the code while debugging to see what is going on to satisfy your concerns.
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

@ Max 

 

ignore this warning about unused byteeables. 

Your datasize is 8 bit so you cannot add a single bit byteenable. i tried that and earned an error from sopc. this message is a bug as MySupport said and should be ignored.  

your chipselect is your 1 bit byteenable here ... 

 

This story about additional avalon access to a slave with less bits data width than the master is old and still present. (maybe a compatibility feature like the A20 gate of x86 cores :lol: )  

use signaltap and monitor your interface between avalon and your ip or memory ... 

 

even when you use IORD there will always be that number of accesses that are needed to get the summ of 32 bits. 

when the slave has 8 bit then it will be 4. 

only when you use IOWR there will be only these accesses you think. 

 

there is a workaround possible. 

if you have a look at the signaltap of your interface then you can see that the byteenables tells you how many valid accesses there will be. 

 

as nails already said, nios is a 32 bit cpu so all data accesses are 32 bit even when only 8 bit of them are valid. 

you might loose performace when you think in 8 bit 

 

if i understand your application right then you need 256bit that are written by the nios cpu and you get 256 bits as a result. 

you could go for an avalon slave that has 2 avs (avalon valid slave) ports. 

1 port for status and control, the other one for data 

tha avs for data is a 256 bit memory the nios will see 8 32bit portions of it. 

it is very easy to implement the nios read write access to that memory with byteenable control. 

now inside your sopc module you have 256 bit that feed your HASH 

if you need help, let me know.
0 Kudos
Altera_Forum
Honored Contributor II
643 Views

 

--- Quote Start ---  

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? 

--- Quote End ---  

 

Have you solve this problem? 

"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 got a similar problem,when I change the "writedata" width to 8. 

If you have any idea,can you tell me? 

Thank you! 

This is my email: 

adream307@gmail.com
0 Kudos
Reply