Prof. Robucci
There are a multitude of options for implementing multi-cycle computation in a slave component and managing handshaking.
First, one must decide if they want to have their custom logic "within" the Platform Design Built System or external.
If you want your logic to be external, then you need to have some component within the the SOPC Module the and create external conduits to communicate to logic external to the system. Even a generic PIO can be used for that purpose. If you need multiple registers, you can use multiple PIO, but it is better to use this provided multi-register slave interface.

A link to the module is provided on the following page https://www.intel.com/content/www/us/en/programmable/support/support-resources/design-examples/intellectual-property/embedded/nios-ii/exm-avalon-memory-slave.html

To have a multi-address slave with your custom logic embedded Example 6-1 from the SOPC Builder User Guide (https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_sopc_builder.pdf) is a good start:
module my_slave_irq_component (// Signals for Avalon-MM slave port “s1” with irq csi_clockreset_clk; //clockreset clock interface csi_clockreset_reset_n;//clockreset clock interface avs_s1_address;//s1 slave interface avs_s1_read; //s1 slave interface avs_s1_write; //s1 slave interface avs_s1_writedata; //s1 slave interface avs_s1_readdata; //s1 slave interface ins_irq0_irq; //irq0 interrupt sender interface); input csi_clockreset_clk; input csi_clockreset_reset_n; input [7:0]avs_s1_address; input avs_s1_read; input avs_s1_write; input [31:0]avs_s1_writedata; output [31:0]avs_s1_readdata; output ins_irq0_irq; /* Insert your logic here */ endmodule
The names provided should be automatically recognized when you import the HDL as a new component, but you can name them whatever you like and provide the port meanings manually as was shown in the discussion session.
Table 1 (page 22 ) describes the signals (https://www.intel.com/content/dam/altera-www/global/zh_CN/pdfs/literature/manual/mnl_avalon_bus.pdf)

A key difference from the discussion session module is that this interface uses an address, which is the address within this module. In C/UBoot you this address is the offset from the component's base address. You can have either real or virtual registers in your design using this sub-address signal.
Note that you can comment out the irq signal and note use it.
For handshaking (synchronizing the master and slave, waiting on results etc...) there are a multitude of options.
One option is to add a control/command register to send signals to your component and a status register that can convey the status back interface to your peripheral. Your peripheral could detect a value change in the control/command register and respond. This would allow that processor to load required registers before the computation starts.
Your component could also directly detect changes in data registers, but this a lazy approach as compared to directly accessing the write signals provided by the bus interfaces.
Two basic options:
Status Register -- If using a status register, your component could flag completion in a status register bit. This requires the processor to poll the status register which can lead to excessive busy waiting.
Interrupt approach -- you could learn to use interrupt signals in the hardware and software. Note the irq signal in the provided template.
Another options is to use additional slave signals that can stall reads and writes. You just need to add the appropriate signals to your component and provide the correct mapping when importing.
(https://www.intel.com/content/dam/altera-www/global/zh_CN/pdfs/literature/manual/mnl_avalon_bus.pdf)


Newer Documentation: Table 9 https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/manual/mnl_avalon_spec.pdf For example, find the timing diagrams in https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/manual/mnl_avalon_spec.pdf describing the use of the waitrequest signal and readdatavalid.
You can also specify fixed wait states while defining your components. This is just the same as fixed wait states for accessing external SRAM as commonly done with microcontrollers. See the same documentation for more information.
As you learn the limitations of communication with your external component, you can learn to implement FIFO interfaces with burst transfers and use of DMA to transfer blocks of data directly to/from your component and external memory, and general strategies to minimize the number of transfers.
https://www.intel.com/content/dam/altera-www/global/zh_CN/pdfs/literature/manual/mnl_avalon_bus.pdf

You can either create a new project or use the one from Discussion 7 (UBOOT).
Mailboxes and control+status and interrupts are options for synchronization and interfacing
Status registers can convey acceptance of command, completion of command and that results are available
Mailboxes can be extended to or be implemented by longer FIFO queues
Here is some example code based on the template.
// File: two_point_fft_component.v // Professor Ryan Robucci // Desc: Implements two-point FFT demonstrates multiple inputs and multiple outputs // Register Map // Offset | Register | Direction | Desc // 0x0 | a0 | Write | First Input Value // 0x4 | a1 | Write | Second Input Value // 0x8 | control | Write | Control/Command // 0x0 | r0 | Read | First Result Value // 0x4 | r1 | Read | Second Result Value // 0xc | status | Read | status module two_point_fft_component (// Signals for Avalon-MM slave port “s1” with irq csi_clockreset_clk, //clockreset clock interface csi_clockreset_reset_n,//clockreset clock interface avs_s1_address,//s1 slave interface avs_s1_read, //s1 slave interface avs_s1_write, //s1 slave interface avs_s1_writedata, //s1 slave interface avs_s1_readdata //s1 slave interface //ins_irq0_irq; //irq0 port interrupt sender interface ); input csi_clockreset_clk; input csi_clockreset_reset_n; input [7:0] avs_s1_address; input avs_s1_read;input avs_s1_write; input [31:0] avs_s1_writedata; output reg [31:0] avs_s1_readdata; //output ins_irq0_irq; /* Insert your logic here */ // Input logic captures values from the bus at offsets 0,4, 8 from the base address // using the provided slave address reg [31:0] a0,a1,control,status,r0,r1; always @(posedge csi_clockreset_clk) begin if (csi_clockreset_reset_n==0) begin a0 <= 1; a1 <= 4; end else if (avs_s1_write) begin case (avs_s1_address) 0: a0<=avs_s1_writedata; 1: a1<=avs_s1_writedata; 2: control<=avs_s1_writedata; endcase end end // Some trivial logic (2-point FFT) with two outputs //Your project should do something over multiple cycles, not something so simple always @(*) r0 = (a0+a1); always @(*) r1 = (a0-a1); // dummy_logic for control and status // The status confirms the control value by repeating the value // The delay through a pipeline of registers serves no purpose other than for demonstration // If you use the component with the embedded processor you would be able to observe the delay // You will not be able to observe this through the terminal... // you would need to add more delay stages reg [31:0] dummy_delay [30:0]; always @(posedge csi_clockreset_clk) begin: dummy_logic integer i; dummy_delay[0]<=control; for (i=1;i<=29;i=i+1) begin dummy_delay[i]<=dummy_delay[i-1]; status<=dummy_delay[29]; end end // Output logic returns values based on the offset from the base address, // offsets 0,4, and 12 are valid here // using the provided slave address always @(*) begin//avs_s1_readdata = 32'hf0f0f0f0; case (avs_s1_address) 0: avs_s1_readdata = r0; 1: avs_s1_readdata = r1; 3: avs_s1_readdata = status; endcase end endmodule
Goto to Tools -> Platform Designer and create a new component from theIP Catalog dialog box.Fill in the details and shown in Figures below for the Component Type, Files and Signal & Interfaces Section.




Make sure there are no errors left under the Messages Tab. Then click on Finish and then add the IP from My Own IP Cores section of the IP Catalog and connect the clock, reset and s1 ports as shown below

Then Assign the Base address and you should not see any errors left in the Messages section. If you see any errors, correct it before going to the next step. Now Save and generate HDL for the design and click on Finish after it is done
In Quartus, Compile the Design and then program the FPGA like we did in the previous sessions.Now open Serial terminal (like Putty for windows) set the COM port and the baud rate as 115200 and make the hardware flow control as None.
After the terminal opens, press the HPS reset button and enter to the UBOOT by pressing any button before the timer ends.

Now, in our code, we have set the initial reset values for a0 and a1 as 1 and 4. So when we display the memory location, you can see the result stored as 5 and -3.
Note: You can find the base address assigned to your IP in the platform design and the offset value is 0xff200000 In the example show, the Base address was 0x00000400, so address 0xff200400 is read.
Now, to check the DUMMY Control and Status functionality, write the value in Control register at location 0xff200408 (0xff200400 + 8) with 1 and so the value on the Status reg can be seen as 1 at location 0xff20040c (0xff200400 + 12).

Now, change the value at location 0xff200400 and 0xff200404 as 7 and 2 using mw. As a result, the value seemingly stored in the memory will be 9 and 5 as shown in the figure below
