Forum Discussion
Simple interfacing between Avalon MM Slave and ARM HPS
Hi all,
I'm trying to create a simple example of a master-slave communication between the onboard HPS (running Linux) and the FPGA fabric using a Cyclone V. The method I've followed so far is this: Firmware: - instantiate the HPS in Qsys with nothing but a LWH2F bridge - write a simple verilog module (flash_led.v) as an Avalon MM Slave (i.e. it has read, readdata, write, and writedata signals) and connect it to the HPS in Qsys - connect everything in a simple top-level verilog module and set the LED output pins to display whatever shows up on the writedata signal Software: - write a simple c program that deals with memory mapping via:int fd = open("/dev/mem", (O_RDWR|O_SYNC));
void* slave_base = mmap(NULL, ALT_LWFPGASLVS_SPAN, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, (off_t)ALT_LWFPGASLVS_ADDR); - also deal with writing to the slave peripheral via: volatile unsigned char* slave_addr = (unsigned char*)slave_base; // the offset of my flash_led component in Qsys is 0x0 so it can be ignored
*slave_addr = some_value; This works for writing to the slave module (I see the LEDs set correctly), but I'm unable to read from it when I try things like "read_value = *slave_addr;" I've seen a few examples online which use the alt_write_word() and alt_read_word() functions, but I haven't been able to get these to do anything for me thus far... Do I need to be doing more with my Verilog module? Is the "address" signal necessary when reading and writing from an Avalon MM slave? I suspect I may have a conceptual misunderstanding of this whole bridging process... Can anyone help me out or point me in the right direction? For reference, here's the simple flash_led.v verilog module I've been using: module flash_led
(
input clock,
input reset_n,
input write,
input writedata,
input read,
output readdata,
output led
);
reg led_internal;
reg stupidconstant = 8'b01010101;
assign led = led_internal;
assign readdata = stupidconstant;
always @(posedge clock) begin
if (!reset_n)
led_internal <= 4'b0000;
else if (write) begin
led_internal <= writedata;
end
end
endmodule John10 Replies
- Altera_Forum
Honored Contributor
Can anyone help me out with this? I've been stuck on the same issue for a few days now... Even if it's just a nudge in the right direction - I'd be happy to know just if my problem is in the software or the firmware.
Thanks, John - Altera_Forum
Honored Contributor
Ok, I solved it. In case anyone has the same confusion, here's the essential parts of the software that I was missing:
This might be overkill, but it works if you just map the entire hardware register space into virtual memory:
Also, I needed to find the address offset of my module that I added as a component to Qsys (read this from the address map)#define HW_REGS_BASE ALT_STM_OFST # define HW_REGS_SPAN 0x04000000 # define HW_REGS_MASK (HW_REGS_SPAN-1)
Then, ignoring error checking, I took care of memory mapping like this:#define MY_MODULE_OFST 0x00000000
Finally, reading and writing worked with:int virtual_base; int fd; fd = open("/dev/mem", (O_RDWR|O_SYNC)); virtual_base = mmap(NULL, HW_REGS_SPAN, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, HW_REGS_BASE);alt_write_word(virtual_base + ((ALT_LWFPGASLVS_OFST + MY_MODULE_OFST) & HW_REGS_MASK), write_val); read_val = alt_read_word(Virtual_base + ((ALT_LWFPGASLVS_OFST + MY_MODULE_OFST) & HW_REGS_MASK)); - Altera_Forum
Honored Contributor
Hi,
can you explain me what shout I set ALT_STM_OFSET ? In "HW_REGES_SPAN" is a span of particular component of IP core or span of HPS LWFPGASLAVES ? What is use of "HW_reges_mask" ? Please help me,I don't have experience to work with Hard processor system ans also don't have much experience to work with Linux.. - Altera_Forum
Honored Contributor
Hi,
I am stuck with the same problem as well. I am able to write data from the HPS to the FPGA, but not able to read data back from the FPGA into the HPS. I am using the Lightweight HPS to FPGA bridge in Cyclone V. I have defined an Avalon-MM slave as follows: -
My C program running in the HPS is as follows: -module scratch_slave_one_byte( input clk, input reset, input write, input writedata, input read, output reg readdata, output reg byte_from_hps, output write_count_w, input byte_into_hps ); reg write_count = 0; assign write_count_w = write_count; always@(posedge clk) begin if (write) begin byte_from_hps <= writedata; end else if (read) readdata <= byte_into_hps ; end endmodule
I connect the readdata [7:0] inputs to 8'b00000011 in the top level module. However, the signal I read into the HPS is always zero. Any help would be great. Thanks! Akshay#include <sys/mman.h> # include <sys/types.h> # include <sys/stat.h> # include <fcntl.h> # include <stdio.h> # include <stdlib.h> # include <unistd.h> # include <stdint.h> # include "hps.h" # include <time.h> # include "socal.h" # define ALT_LWFPGASLVS_OFST 0xff200000 # define HW_REGS_BASE (0xfc000000) # define HW_REGS_SPAN (0x04000000) # define HW_REGS_MASK (HW_REGS_SPAN-1) # define MY_MODULE_OFST 0x00000000 # define WRITE 0x00 # define READ 0x04 volatile unsigned char* custom_slave; void *virtual_base; int main() { int fd; if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) { printf( "ERROR: could not open \"/dev/mem\"...\n" ); return( 1 ); } virtual_base = mmap(NULL, HW_REGS_SPAN, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, HW_REGS_BASE); if( virtual_base == MAP_FAILED ) { printf( "ERROR: mmap() failed...\n" ); close( fd ); return( 1 ); } custom_slave = (unsigned char*)(virtual_base + ( (ALT_LWFPGASLVS_OFST + MY_MODULE_OFST + WRITE) & (HW_REGS_MASK))); custom_slave = 0x06; //WRITE BYTE INTO FPGA unsigned char value = custom_slave; //READ BYTE FROM FPGA printf("%d\n",value); //value = alt_read_word(virtual_base + ((ALT_LWFPGASLVS_OFST + MY_MODULE_OFST) & HW_REGS_MASK)); //printf("%d\n",value); cleanup: if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) { printf( "ERROR: munmap() failed...\n" ); close( fd ); return( 1 ); } close(fd); return 0; } - Altera_Forum
Honored Contributor
I am now able to read and write 32 bit words using the lightweight HPS to FPGA bridge!
Attaching my code if it might be useful to anyone: - I have defined a custom Avalon slave defined as follows below. Whenever read, it sends 32'hA5A5A5A5 as the readdata [31:0].
Top level module is as follows below. I am connecting the top 4 MSB's sent from the HPS to the LEDs on the DE0-Nano-SoC board.module scratch_slave_32_bits_rw ( // No conduit input clk, input reset, input read, output reg readdata, input write, input writedata, output reg hps_to_fpga_32_bits //conduit ); reg arbitrary_constant = 32'hA5A5A5A5; always@(posedge clk) begin if(read) readdata <= arbitrary_constant; else if (write) hps_to_fpga_32_bits <= writedata; end endmodule
Finally, my C program running in the HPS is as follows:-module quartus_scratch_1( input CLOCK_50, output led_values, output hps_memory_mem_a, output hps_memory_mem_ba, output hps_memory_mem_ck, output hps_memory_mem_ck_n, output hps_memory_mem_cke, output hps_memory_mem_cs_n, output hps_memory_mem_ras_n, output hps_memory_mem_cas_n, output hps_memory_mem_we_n, output hps_memory_mem_reset_n, inout hps_memory_mem_dq, inout hps_memory_mem_dqs, inout hps_memory_mem_dqs_n, output hps_memory_mem_odt, output hps_memory_mem_dm, input hps_memory_oct_rzqin ); wire hps_to_fpga_32_bits; assign led_values = hps_to_fpga_32_bits; soc_system soc_system_inst_1( .clk_clk(CLOCK_50), // clk.clk .memory_mem_a(hps_memory_mem_a), // memory.mem_a .memory_mem_ba(hps_memory_mem_ba), // .mem_ba .memory_mem_ck(hps_memory_mem_ck), // .mem_ck .memory_mem_ck_n(hps_memory_mem_ck_n), // .mem_ck_n .memory_mem_cke(hps_memory_mem_cke), // .mem_cke .memory_mem_cs_n(hps_memory_mem_cs_n), // .mem_cs_n .memory_mem_ras_n(hps_memory_mem_ras_n), // .mem_ras_n .memory_mem_cas_n(hps_memory_mem_cas_n), // .mem_cas_n .memory_mem_we_n(hps_memory_mem_we_n), // .mem_we_n .memory_mem_reset_n(hps_memory_mem_reset_n), // .mem_reset_n .memory_mem_dq(hps_memory_mem_dq), // .mem_dq .memory_mem_dqs(hps_memory_mem_dqs), // .mem_dqs .memory_mem_dqs_n(hps_memory_mem_dqs_n), // .mem_dqs_n .memory_mem_odt(hps_memory_mem_odt), // .mem_odt .memory_mem_dm(hps_memory_mem_dm), // .mem_dm .memory_oct_rzqin(hps_memory_oct_rzqin), // .oct_rzqin .scratch_slave_32_bits_rw_0_conduit_end_export(hps_to_fpga_32_bits) // scratch_slave_one_byte.export ); endmodule
Regards, Akshay#include <sys/mman.h> # include <sys/types.h> # include <sys/stat.h> # include <fcntl.h> # include <stdio.h> # include <stdlib.h> # include <unistd.h> # include <stdint.h> # include "hps.h" # include <time.h> # include "socal.h" # define HW_REGS_BASE (0xfc000000) # define HW_REGS_BASE (0xfc000000) # define HW_REGS_SPAN (0x04000000) # define HW_REGS_MASK (HW_REGS_SPAN-1) # define RW_SLAVE_OFST 0x00000000 volatile uint32_t *custom_slave_rw; void *virtual_base; int main() { int fd; if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) { printf( "ERROR: could not open \"/dev/mem\"...\n" ); return( 1 ); } virtual_base = mmap(NULL, HW_REGS_SPAN, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, HW_REGS_BASE); if( virtual_base == MAP_FAILED ) { printf( "ERROR: mmap() failed...\n" ); close( fd ); return( 1 ); } custom_slave_rw = (uint32_t*)(virtual_base + ( (ALT_LWFPGASLVS_OFST + RW_SLAVE_OFST) & (HW_REGS_MASK))); uint32_t read_value = *custom_slave_rw; printf("Value read = 0x%08x\n",read_value); //READ 32--BIT-WORD FROM FPGA uint32_t write_value = 0x69696969; custom_slave_rw = write_value; //WRITE 32-BIT-WORD INTO FPGA printf("Value written = 0x%08x\n\n",write_value); cleanup: if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) { printf( "ERROR: munmap() failed...\n" ); close( fd ); return( 1 ); } close(fd); return 0; - Altera_Forum
Honored Contributor
Hello Akshay, Im using your code to test. But I have an error. {Port "scratch_slave_32_bits_rw_0_conduit_end_export" does not exist in macrofunction "soc_system_inst_1"}. Do you have any idea ? Thank you.
- Altera_Forum
Honored Contributor
Hey,
1) Please check if you included the Avalon slave in the Qsys component. 2) Check if you have exported the port "scratch_slave_32_bits_rw_0_conduit_end_export" as Conduit in the Avalon slave Qsys component. You could use this well explained blog for reference: https://zhehaomao.com/project/2014/01/02/fpga-series.html Regards, Akshay - Altera_Forum
Honored Contributor
Thank you Akshay !
- Altera_Forum
Honored Contributor
I really don't get why HW_REGS_BASE (0xfc000000) must be used?!
Can anybody explain this? This is the Base Address of the Coresight System Trace Macrocell or not?! cant we mmap the ALT_LWFPGASLVS_OFST instead? - APaci1
Occasional Contributor
Hi to every one! I'm attempting to do something similar to this question.. I want read/write QSYS on chip memory dual access from HPS running a C app under Linux
I make a bootable sd card with angstrom 2018.06 and it runs fine on my self-developed card with CycloneV Soc (!! hard work but it is done!!)
So, I connected DS-5 V29 to my card via remote system SH Connection, it works fine following Hello Word for Linux app tutorial!! DS-5 woks well.
Then I search to access to ONCHIP mem follwoing examples from LED access as you show here ! Virtual memory access and so on
MY PROBLEM is to determine a valid environment IN DS-5 to do it, in terms of libs to include!!!! THERE IS A BUNCH of mman.h, fcntl,h etc etc.. they are under my Linaro distro spl directory
- If I use GCC compiler , I obtain errors on valid simple code C and I must use ARM-5 compiler
- Then, I use ARM-5 compiler, but it doenst' find std.io basic libraries, options 0_SYNC and other options in mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0) are not acks correctly
- THERE IS A OVERVIEW OR A MANUAL TO CREATE A RELIABLE DS-5 ENVIRONMENT to works fine writing HPS C APP???? It is difficult to link and search a bunch of libraries without a guide! Linux libs for mman.h are interconnetcted and so cannot be simply copied under the project folder, but probavly they must set as path..
- I cannot find any tutorial about this issue!
Do you have solved this bunch?
Thanks in advance for any suggestion!