02-12-2018 01:14 AM
Hi,
I am referring the BRAM example code that is available in Xilinx Vivado. I want to use BRAM with dual port such that PL compute some data and store it to some address location, from Port_in on PORT A of BRAM and PS can read that address and fetch data at its out pin on PORT B of BRAM..
I have simulated the code on Vivado to access the data on BRAM from PL end but I am confused that what changes I will have to perform to access it from PS and where should I write PS code to access BRAM specific address and sense the data out from BRAM output port to read it on PS and use that BRAM data in other computation..
If there's any demo code or any reference guide to refer and access the data written from PL to BRAM and access on PS then it will be helpful...
Thanks,
Dhara
02-12-2018 01:49 AM
Once you have the PL BRAM connected to the PS via port interface (GP port for example), then you can use simple reads to do this.
Xil_In32(addr);
02-12-2018 05:09 AM
Hi,
In this case what will be the IP block implementation.. I will have to used the reference from an example code of BRAM in Vivado.. I have attached the screen shot of one such implementation I am working on..
Is there any changes you would suggest or advice any corrections in it..also any where in PL code to implement the functionality to access the data from PS point of view.. can you share any demo code for PL... I mean once we configure the IP blocks in Vivado in what manner I should initiate PS to take up the data from specific location of BRAM with the hardware platform designed in the PL (as the one in attached file)..
Thanks.
02-14-2018 06:36 AM
Hi @dpandya9488,
You will just need to read at the address of the BRAM in your application.
Regards,
02-14-2018 01:16 PM
it's not really difficult, I googled 'vivado bram tutorial'
checkout for example this youtube video, from 6:00 on he shows how to write/read from BRAM
in short, you use the functions in 'xil_io.h'
Xil_out8, Xil_Out16, Xil_Out32 to write
Xil_in8, Xil_in16, Xil_in32 to read
then you need to find the address of your BRAM -> check xparameters.h
this file is generated for you by SDK, based on the address map you can see in Vivado
I'd highly recommend you to go through UG940, you'll win all the time back later
this tutorial by Silica for the Zedboard is one of the best around, I learned a lot from it.
UG1165 is also a good one to check
02-15-2018 04:06 AM
@dpandya9488 I am also interested in doing similar kind of task, i.e to perform some computation in PL and store the data in Dual Port BRAM and then program PS to read the data through AXI from the other port of BRAM. The program in the PL has to be packaged in IP in Vivado and added in the Block Diagram. I guess this is the same as you are doing now, right ?
I am not sure which board you have but I have tested a program on Z-Turn 7020 board using a dual port BRAM and two AXI BRAM Controllers. I programmed the PS in SDK using Xil_in and Xil_out as explained by @ronnywebers. I have initialized a portion of BRAM through PS using one AXI BRAM Controller and then read the same BRAM locations through PS using the other AXI BRAM Controller. The program works well on Z-Turn 7020. Please have a look in attachment, maybe it is helpful to some extent. In this example I have two AXI BRAM Controllers because I am reading and writing BRAM through PS but if we are interested to write data from the logic we have packaged in PL then we don't need the second AXI BRAM Controller.
02-17-2018 11:45 AM
Hi @ronnywebers
Many thanks for the suggestions..The video link and tutorial are very helpful to access (read and write) BRAM from PS side.. I also checked the UG940 and implemented and checked its Lab3 tutorial..
I am little confused with BRAM accessibility from PL side..Means if I want to write on BRAM directly from PL then I should leave that BRAM port unconnected without that port being routed even to AXI BRAM Controller.
If I am assigning PortB of dual port BRAM to PL (to write and read) and PortA of the same dual port BRAM to PS, then PL side BRAM port (BRAM_PORTB) should be left unconnected (for direct PL access) and PS side port will be connected through AXI BRAM controller --> AXI Interconnect -->PS..
The IP block created and shared attached in this one (same as in the original post).. Is that the correct method..? Because it ends up with error when I am trying to generate its bitstream and export it to SDK.
Thanks,
Dhara
02-17-2018 11:51 AM
Hi @Anonymous,
Thanks for the suggestion..
I am implementing this on ZC702 Evaluation Board.. I need to load BRAM on boot-up with some default data that will be later accessed by the functionality that need to be implemented on PL.. PL will compute the data and again load it on BRAM and at a particular instance, that data will be read by PS..
This is the implementation I am working on with PL-PS and BRAM accessibility..
Thanks,
Dhara
02-19-2018 12:08 AM
@dpandya9488, you're not telling which error you get when trying to create a bitstream.
But anyway, you should connect 'something' to the PORTB of your BRAM, instead of connecting it to a port. You'll need to create some custom IP in HDL (VHDL, Verilog or HLS) which does your computation on the BRAM data.
you could for example build an FSM that reads the data on each address, adds 1 to it, and writes it back. Then you'll also need something to trigger a 'start' of this caluclation, and optinally signal an end.
02-19-2018 01:48 AM
Hi @ronnywebers
I have attached the error that is observed when I generate bitstream for the IP blocks created for PL read and PS write/read access.. You mean to say there should be some logic/ process written in HDL for PL that writes PortB and then I should generate bitstream..!! Only IP block instantiation in this case is not sufficient..?
Please check the error and suggest me what corrections I should do to resolve this issue..
02-19-2018 02:27 AM - edited 02-19-2018 02:28 AM
@dpandya9488 sorry, I can't make much of the error, but I think it's because you wired up port B of the BRAM just to some output port. Now not all signals of this PORT B are driven correctly.
anyway, you wrote :
You mean to say there should be some logic/ process written in HDL for PL that writes PortB and then I should generate bitstream..!! Only IP block instantiation in this case is not sufficient..?
yes, you need to write a custom IP to do that, as such IP does not exist in the Xilinx IP library. At the moment, your bd just connects portB of the BRAM to a 'port'. So there won't be happening much.
If you first want to check if you can read/write from the PS, you could start with having only 1 BRAM interface enabled on the BRAM controller, and the block memory generator set to 'single port RAM'. That way, you can generate a bitstream, and test if PS can read/write to the BRAM. I'd recommend you to do this step first.
Up next you would change the BRAM to 'true dual port RAM'. Then you need to create & package a custom IP that connects to PORTB. That IP would contain an FSM that handles read/write and the calculation. Then also you would need to 'trigger' your FSM from the PS to tell it to start it's cycle. That 'trigger' can be a simple AXI GPIO line, or an EMIO line, or an AXI Lite register interface. You could do the same for a 'done' flag.
I'm not sure if there's any such tutorial on a custom IP that interfaces BRAM is availble from Xilinx or on the web, mabye @florentw has some suggestion?
A great tutorial for a simple custom AXI Lite IP that does a multiplication in PL is this one.
02-20-2018 12:29 AM
@ronnywebers I guess in this case the custom IP would not have AXI interface because BRAM is located in the PL part, right ?
In general in FPGAs, in order to write data to BRAM we simply need "address" and "data" to be written to the BRAM, but before sending data to the BRAM we need to be sure that write enable "wr_e" is high which enables BRAM for write operation. Sometime read and write clocks are different i.e, clk_wr and clk_rd but some other time they are same. I guess in custom made IP we only care about clk_wr, right ? and should not be connected to clk_rd because it would be operated by PS, am I right ?
02-20-2018 01:19 AM - edited 02-20-2018 01:26 AM
Can you explain a bit more what you mean with this phrase :
I guess in this case the custom IP would not have AXI interface because BRAM is located in the PL part, right ?
almost everything you see in your BD, except for the 'Processing System block' (= PS) itself, is located in the PL... it's all HDL code 'behind the scenes', except for the PS which is 'hardened' IP. (Note that BRAM also uses some 'hard' memory block, but has some 'hdl' stuff stiched around it, together this makes up the BRAM IP) ... so the AXI interconnect, the BRAM controller, ... everything is in the PL.
to access BRAM from the PS, you pass through a 'BRAM controller', which basically translates AXI transfers to the BRAM interface.
to access BRAM from the PL, it depends on what you want to do : if you want to read let's say 1024 values from the BRAM, add one 1 each value, and write the result back to the same (or another location), you need to control the rd, wr, addr, ... signals, and make sure all other signals (byte enables, ..) are properly handled too - check the IP's user guide to learn the details of each signal, and how to handle them. If you only want to write value from the PL to the BRAM, and read them from the PS, then your 'PL IP' can connect the rd to '0' for example, as it never has to read the memory.
having different rd/wr clocks depends on your system design : some desings run on 1 single clock, so both 'sides' of the BRAM use the same clock. But a 'true' dual port RAM allows to have a different clock on each side of the BRAM. It all depends on what you want to do.
** kudo's are the only salary that forum volunteers get from Xilinx, don't forget to throw some from time to time :-) **
02-22-2018 07:07 AM - edited 02-22-2018 07:12 AM
Hello,
I just made a custom ip, package it and then include it in the Block Diagram. I am reading from Port A of BRAM from PS side and writing from Port B of BRAM.
Basically on the rising edge of the clock I am updating the data and address by addition of four as shown below. The data and address signals are initialized by 32 bit zeros, please see the source code in attachment.
dinb_s <= dinb_s + conv_std_logic_vector(4, 32);
addrb_s <= addrb_s + conv_std_logic_vector(4, 32);
The output (when reading using 8 bit function in SDK) on the serial should be
Base address + 00 : 00
Base address + 01 : 00
Base address + 02 : 00
Base address + 03 : 00
Base address + 04 : 04
Base address + 05 : 00
Base address + 06 : 00
Base address + 07 : 00
Base address + 08 : 08
-
-
-
But I am getting
The values in BRAM at address 40000000 is 0
The values in BRAM at address 40000001 is C0
The values in BRAM at address 40000002 is 13
The values in BRAM at address 40000003 is 0
The values in BRAM at address 40000004 is 4
The values in BRAM at address 40000005 is A0
The values in BRAM at address 40000006 is 18
The values in BRAM at address 40000007 is 0
The values in BRAM at address 40000008 is 8
The values in BRAM at address 40000009 is 80
The values in BRAM at address 4000000A is 1D
The values in BRAM at address 4000000B is 0
The values in BRAM at address 4000000C is C
The values in BRAM at address 4000000D is 80
The values in BRAM at address 4000000E is 22
The values in BRAM at address 4000000F is 0
Every forth looks fine (not all, please see log file in attachment), the three values in between should be zero but they are not zero.
Attachment include the source code of custom ip, it's simulation, and the serial terminal output. Could anyone look at this please ?
02-22-2018 08:40 AM
just did a quick check :
* what does the memory window in SDK display if you look at the memory range (0x40000000)? (using JTAG)
* what's the ouput if you read 32 bits at a time, at base@+0, @+4, @+8, ...
* check your fsm code : p_state_out process : you have no default values before your case statement, so you either need to define all outputs for/in each state, or add default values before the case, so there's no ambiguity
i.e. you do not define what value 'web' must have in the states 'init_s' and 'done_s', I would not leave it to the compiler to choose. (ie. check page 115 in this doc, just googled one)
02-26-2018 12:42 AM
Hi, I have added 'web' in both states 'init_s' and 'done_s'. I am reading in 32 bit from BRAM but I am not sure why I am getting 3 bytes in the start and 4 bytes latter. The data values should be 00000000, 00000004, 00000008, 0000000C, ... but they are not as shown below.
Delay Function Start
Delay Function End
Now Reading BRAM
The values in BRAM at address 40000000 is 118000
The values in BRAM at address 40000004 is 12E004
The values in BRAM at address 40000008 is 142008
The values in BRAM at address 4000000C is 15800C
The values in BRAM at address 40000010 is 16E010
The values in BRAM at address 40000014 is 184014
.
.
The values in BRAM at address 400002BC is FDE2BC
The values in BRAM at address 400002C0 is FF42C0
The values in BRAM at address 400002C4 is 100A2C4
The values in BRAM at address 400002C8 is 10202C8
The values in BRAM at address 400002CC is 10362CC
.
.
Attachment include simulation, source code and also log file.
02-27-2018 03:47 AM
@Anonymous
there are effectively 4 bytes, but the upper 2 are '00' -> with the current formatting you use, the printf will not show these
if you want to see something like 0x00123456 you have to tell printf this : 0x%08x
for example :
memP = (u32 *)0x10000000; xil_printf("@0x%08x : 0x%08x\n\r", memP, *memP);
for more examples google 'printf formatting'
so the lower 2 bytes look ok, I'm not sure yet why the rest is wrong (don't have enough time to check in detail). But to debug your issue, I would do the following :
1) decouple your custom IP that writes into the BRAM, and add constants to the 'write side' : disable write, data = 0x000000, and so on, so you PL does nothing in the BRAM.
2) then check if you can write some values like 0x12345678 to the BRAM from PS, and read them back from the PS. At least then you know that this side is already ok. When that works, proceed to 3
3) add back your custom IP that writes to BRAM, and just read the data, because your fsm will probably be finished by the time your PS code runs through it's main loop. The FSM starts as soon as your bitstream is loaded.
4) if you experience issues, add an ILA. That's a tool that you MUST learn to use, to debug any issues. It works great, and it is your indispensible 'oscilloscope' to see any problems, up to the clock cycle. check UG936 and UG908 for more info on the ILA.
note : you could also add an 'enable' and a 'restart' to your custom IP through AXI GPIO pins, so you can control when your custom IP kicks in. Now it just starts as soon as the PL bitstream is downloaded, even before the PS can access the BRAM. Also, it runs once then stops. But you can keep this expansion for later
03-07-2018 05:07 AM - edited 03-07-2018 07:16 AM
Hello, I am still having problem in writing data to BRAM from PL side. I am writing data "ABCDE000" with increments to 1000 locations staring from the base address. The simulation works well as shown in attachment.
dinb_s <= dinb_s + conv_std_logic_vector(4, 32);
addrb_s <= addrb_s + conv_std_logic_vector(4, 32);
When I am reading BRAM data from 1000 locations using AXI BRAM CTRL 0 in SDK and printing using UART, I am not reading the right data what I have written in the BRAM using state machine whose code is also in attachment together with Block Diagram.
[Wed Mar 07 13:47:15.432 2018] Delay Function
[Wed Mar 07 13:47:15.432 2018] Reading BRAM using AXI BRAM CTRL 0 after delay
[Wed Mar 07 13:47:15.432 2018] The values in BRAM at address 0x40000000 is 0xAC262000
[Wed Mar 07 13:47:15.432 2018] The values in BRAM at address 0x40000004 is 0xAC2E0004
[Wed Mar 07 13:47:15.432 2018] The values in BRAM at address 0x40000008 is 0xAC35C008
.
.
.
.
[Wed Mar 07 13:47:16.785 2018] The values in BRAM at address 0x40000430 is 0xB4546430
[Wed Mar 07 13:47:16.785 2018] The values in BRAM at address 0x40000434 is 0xB45C2434
During the IP packaging I am getting warning regarding clock and reset signals in Vivado (see attachment), which is also attach. Do we need to map clock and reset signals some other way in IP packaging ?
In addition I also did another test by removing the IP which I used for writing the BRAM from FPGA. I write the BRAM directly from SDK software using AXI BRAM CTRL 0. I read the BRAM before and after writing data, see the attached log file. It looks ok but I am not sure why it does not work when I write data from IP. Could there be wrong addressing ? I guess the base address of AXI BRAM CTRL 0 is the one how ARM see the AXI BRAM CTRL 0 which is 0x40000000 but when we are writing from the FPGA side using own IP then the base address is simply 0x00000000, right in 32 bit addressing ?
03-08-2018 07:22 AM - edited 03-08-2018 07:24 AM
@Anonymous
I guess the base address of AXI BRAM CTRL 0 is the one how ARM see the AXI BRAM CTRL 0 which is 0x40000000 but when we are writing from the FPGA side using own IP then the base address is simply 0x00000000, right in 32 bit addressing ?
-> that is correct
didn't have much time yet to do a detailed analysis. Did you put an ILA between your IP and the BRAM to see what's happening - sometimes simulation can be different from actual synthesized result.
I checked your fsm quickly, don't have much time at the moment, so I might be wrong about some things :
* in init_s you activate rstb (reset is active high). But you only deactivate it in done_s. If I see that correctly, your reset is active during data_s and send_s? Then any write will be ignored... Can you check that in your simulation?
* in data_s you activate 'web', but in send_s you turn them back off, while you copy there your dinb_s to dinb. I think 'web' should be activated in send_s
use your testbench and/or ILA to compare the waveforms with the required ones (from UG473 and PG058) :
03-08-2018 07:28 AM
regarding the warning (ASSOCIATED_BUSIF bus parameter missing), check this post for explanation and a solution. you can ignore the warning for now (it will not be the cause of your problem), but if you want to get rid of the warning, check the post.
03-11-2018 04:13 AM
Now it works. In the IP which I package, I just change the I/O names for clk and rest_n to some other and then it become a normal I/O and I don't get the warnings for clock and reset during the IP packaging which I got before.
Second I also correct the reset signal. The reset signal is active high has to be low during write operation to BRAM and also "web" has to be high (write enable) during the write operation.
I just notice with the following code in design that is under a process whose sensitivity list is any change in clock or reset.
dinb_s <= dinb_s + 1;
addrb_s <= addrb_s + 1;
The data and address should increment by 1 upon rising edge of clock. The address and the data both are 32 bits (4 bytes). Does it mean that the BRAM need four clock cycles to write the 4 byte of data to any particular location define by the 32 bit address ? because I am reading the following using printf function in SDK.
BRAM at address 0x40000000 is 0x00000004
BRAM at address 0x40000004 is 0x00000008
BRAM at address 0x40000008 is 0x0000000C
BRAM at address 0x4000000C is 0x00000010
BRAM at address 0x40000010 is 0x00000014
BRAM at address 0x40000014 is 0x00000018
03-11-2018 09:53 AM - edited 03-11-2018 09:54 AM
ok good to hear that it's working.
regarding the reset & clock : you can read in UG1118 how these can be inferred, if you follow some naming conventions (check page 13 and further). Because you now gave them different names, they will no longer be recognised as reset & clock signals. Which works fine, but if you do infer reset and clock signals, Vivado can do some additional checks for you when you integrate the IP in a larger (block) design.
if the databus to your memory is 32 bits wide, you can write a single 32-bit word in 1 clock cycle to the memory. So it will not write byte by byte in 4 cycles, but te 32-bit in 1 cycle.
I don't have your latest code, so not sure what's happening. But I'd recommend to put an ILA on the interface now that it's almost working, and see exactly what's happening. The ILA are your eyes into the FPGA :-)
03-12-2018 08:24 AM - edited 03-12-2018 08:26 AM
The main code which writes the data in BRAM upon every rising edge of clock is the following. Both signals are 32 bits.
dinb_s <= dinb_s + x"00000001";
addrb_s <= addrb_s + 1;
The SDK code is here
// Reading data from BRAM using AXI BRAM CTRL 0
for (i = 0; i < 250 ; i ++)
xil_printf("The values in BRAM at address 0x%08x is 0x%08x \n\r ", XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + i*4, Xil_In32 (XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + i*4) );
I also checked by i*1 in the above code which should read every memory location but it hang after the first read.
03-12-2018 09:53 AM - edited 03-12-2018 09:56 AM
yes, it's normal that your CPU hangs when you try the i*1, you must respect what is called 'memory alignment'.
I just googled quickly, and came accross this which explains it a bit more graphical. Check the image at the bottom, left : this is how you should read from memory :
Regarding the contents that still look wrong : I think your hdl code generates a 32-bit address, so try with incrementing the address by '4' instead of '1', and see if the results are then as expected.
03-14-2018 01:41 AM
@ronnywebers, Thanks. Now it works. I increment the address by four in the state machine and re-package the IP after simulation. The IP generate different patterns selected by PL switches. The pattern selection is also connected to LEDs. Following is the output with two different pattern select.
dinb_s <= dinb_s + x"04000004";
addrb_s <= addrb_s + x"00000004";
The values in BRAM at address 0x40000000 is 0x00000000
The values in BRAM at address 0x40000004 is 0x04000004
The values in BRAM at address 0x40000008 is 0x08000008
The values in BRAM at address 0x4000000C is 0x0C00000C
The values in BRAM at address 0x40000010 is 0x10000010
The values in BRAM at address 0x40000014 is 0x14000014
The values in BRAM at address 0x40000018 is 0x18000018
The values in BRAM at address 0x4000001C is 0x1C00001C
The values in BRAM at address 0x40000020 is 0x20000020
dinb_s <= dinb_s + x"04040404";
addrb_s <= addrb_s + x"00000004";
The values in BRAM at address 0x40000000 is 0x00000000
The values in BRAM at address 0x40000004 is 0x04040404
The values in BRAM at address 0x40000008 is 0x08080808
The values in BRAM at address 0x4000000C is 0x0C0C0C0C
The values in BRAM at address 0x40000010 is 0x10101010
The values in BRAM at address 0x40000014 is 0x14141414
The values in BRAM at address 0x40000018 is 0x18181818
The values in BRAM at address 0x4000001C is 0x1C1C1C1C
The values in BRAM at address 0x40000020 is 0x20202020
03-14-2018 02:54 AM
that's great news! Guess you learned a lot from this :-)
so if possible you can close this thread by accepting one of the answers that helped you most, maybe one of the last answers.
03-14-2018 03:07 AM
Not sure how I can accept as answer. I don't see any option for accepting answer or solved, maybe because the original post was initiated by someone else ?
03-14-2018 03:33 AM
yes you're right about that, I forgot you didn't initiate the post :-) never mind, not sure if @dpandya9488 is still following this.
03-14-2018 08:02 AM - edited 03-14-2018 08:03 AM
Anyhow I learned a lot from this post regarding "BRAM Access to write it from PL and read from PS and compute that data". I am able to generate patterns using FSM in the custom made non-AXI IP which I connect to Port B of the BRAM to write data to BRAM. Later I read the data from SDK using AXI BRAM Controller connected to Port A of the BRAM. The output is already shown in my earlier post.
Thanks @ronnywebers for all your replies and comments which were really helpful.
03-20-2018 01:06 AM
Hi @ronnywebers
I do refer the suggestions and have generated BRAM read from PS side by writing data from PL side.. I am trying to generate interrupt from PL to PS after around certain bulk data written from PL and read it from PS.. Kind of interrupt based write-read operation..
Next I want to do the other way round.. Write from PS on BRAM and generate PS to PL interrupt and PL read those addressed on BRAM..
Many Thanks,
Dhara