Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
11 years ago

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

John

10 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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:

    #define HW_REGS_BASE ALT_STM_OFST
    # define HW_REGS_SPAN 0x04000000
    # define HW_REGS_MASK (HW_REGS_SPAN-1)

    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 MY_MODULE_OFST 0x00000000

    Then, ignoring error checking, I took care of memory mapping like this:

    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);

    Finally, reading and writing worked with:

    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's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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: -

    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

    My C program running in the HPS is as follows: -

    #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;
    }

    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
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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].

    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

    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 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

    Finally, my C program running in the HPS is as follows:-

    #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;
    

    Regards,

    Akshay
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored 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's avatar
    APaci1
    Icon for Occasional Contributor rankOccasional 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!