Jump to content
  • 0

Create an IP that can Partly be Controlled with Vivado SDK and Partly be Controlled by a Physical Switch that Does Not Rely on Vivado SDK


electronicsdevices

Question

So far, I've been relying on this video if I want to make custom IP and control it with just the SDK

 

It's gotten me through many things so far, but now I want to implement a design like this:

 

5980d043bf5a1_Screenshotfrom2017-08-0114-01-55.png.3ca52dacb209356e931844d6994e2262.png

Previously, I had hardcoded the matrix entries for matrices A and B. What I want to do is to use Vivado SDK to write what the matrix entries of matrices A and B are. Then, I want to flip a physical switch on the BASYS 3 board and use the Verilog logic I have already got working on the Verilog-only implementation of this model to multiply matrices A and B together and store the contents in matrix Z. The results of the multiplication can be viewed by flipping another physical switch, at which point they will be displayed on the four 7-segment displays (for simplicity, the matrix entries are between 1 and 2, so the largest possible resultant entry is 8).

The matrix A, B, and Z entries I know how to read and write with Vivado SDK. The way I usually do this is to create an IP with an AXI4 interface. However, the twist is that I want to control a few switches and an LED WITHOUT using Vivado SDK. I know that I could use the LED and Switch IP's by drag-and-dropping them onto a block design, but I only know how to control them with Vivado SDK. How can I create an IP where during runtime, I can have the FPGA (not Vivado SDK) monitor whether a switch has been pushed while still being able to change the contents of matrices A and B with Vivado SDK. Also, what do I need to do to be able to implement the a,b,c,d,e,f,g,and dp parts of the 7-segment display in the design?

In other words, in the video, he has a simple interface, no physical LEDs or switches used:

 

module myAdder8bit(

input CLK,

input A,

input B,

output S

);

and in the AXI4 wrapper, the user logic was something like this:

myAdder8bit UIP ( .CLK(S_AXI_ACLK),  .A(slv_reg0[7:0], .B(slv_reg0[15:8], .S(adder_out   )  ;

all the parameters he needed, he found or made in the AXI4 wrapper verilog file.

 

If he amended the interface as follows:

module myAdder8bit(

input CLK,

input A,

input B,

input physicalSwitchOnTheBoard,

output S

);

and in the AXI4 wrapper, the user logic was something like this:

myAdder8bit UIP ( .CLK(S_AXI_ACLK),  .A(slv_reg0[7:0], .B(slv_reg0[15:8],  .physicalSwitchOnTheBoard( IDONTKNOWWHATTOPUTINHERE    )   ,  .S(adder_out   )  ;

 

what would I substitute in for "IDONTKNOWWHATTOPUTINHERE"?

 

Link to comment
Share on other sites

Recommended Posts

Create a verilog module in the project manager. Once it is in the project, go into your block design, right click, and select "Add Module". This only works in Vivado 2016 and 2017. It's a relatively recent feature where Vivado is able to interpret verilog and vhdl source files as IP. Parameters to your modules will show up when you double click the "IP" to edit it's settings as well.

Link to comment
Share on other sites

A few notes, I wouldn't use this in a project I was going to release, it would definitely be better to create a custom AXI IP, and I can go into how to edit the template later if you want (we have some tutorials, but they only really cover PS-PL data transfers). I find this method fantastic for prototyping something. Important addendum is that Vivado cannot package modules added like this into IP, at least in 2016.

Link to comment
Share on other sites

I apologize if a lot of this is redundant with the tutorial.

First, create a new axi4 peripheral, this you probably know.

Spoiler

package.png.e41f3a55c3779c84ccb900bf6d8a6e10.png

You can edit the number of registers and size of them, in case you need more than 4x32 bits in your interface. I believe that adding more registers or making them wider does not affect your bandwidth over AXI, so this is just a convenience.

Spoiler

settings.png.8dd4c238f9c2d78115c5953bd3f2b845.png

MAKE SURE you select edit IP before finishing.

Spoiler

edit.png.2c19eae033dfc45427725429a96e24d9.png

More to come...

Link to comment
Share on other sites

@artvvb

Here is my first attempt at implementing your suggestion. After putting this IP in a block design however, it does not prompt me to select pins for switches and LEDs. You had mentioned I need to edit both AXI files. I usually only edit the myipname_v1_0_S00_AXI.v. I tried to  copy and paste my changes from this AXI file to myiptest.v, but I get an error because myiptest.v does not have the slave registers I need, so I left that entire section blank. Where do I go from here?

MMultAXI4v

myiptest_v1_0_S00_AXI.v

myiptest.v

Link to comment
Share on other sites

In the template, you can find a few relevant areas when it comes to customization:

First, the place to put any parameters you want to add.

//Users to add parameters here

Second, the place to put any additional ports you want to add. For instance, if you wanted to connect LEDs to your IP.

// Users to add ports here
output [3:0] led,

If you wanted to connect to switches.

input [3:0] sw,

If you want ports and/or parameters to the IP, you need to add them to the port maps of both <myip>_v1_0.v and <myip>_v1_0_S00_AXI_inst. Make sure to edit the instantiation of the "Axi Bus Interface S00_AXI" as well.

 

Relevant code in myip_v1_0_S00_AXI:

If you want to configure one of the register addresses to communicate from PL to PS, you will need to edit the synchronous process on lines 211-261 of this file. Comment out lines referring to the slv_reg# that you want to replace. If you want to change the name of the slv_reg#, make sure to replace it in lines 365-368 as well. The slave register can be replaced with a wire output of a verilog module you want to instantiate, for instance.

 

This is key to getting communication set up:

If you want a particular address to be written from the processor: use the slv_reg with the appropriate number (with 32 bit wide registers, slv_reg1 would be located at BASEADDR+4 when you are using Xil_Out32) in code added in the section at line 392 (Add user logic here).

If you want a particular address to be read from the processor: replace all instances of that slave register's name when it appears on the right hand side of an assignment in the _S00_AXI file with your own signal. Comment out all instances where that slave register appears on the left hand side of an assignment.

Do not touch any other predefined signals in either file, unless you really know what you are doing.

You can basically do whatever you want in the Add user logic here area, as long as you keep in mind how your IP will be communicating with the rest of your design. AXI lets you communicate with the processor (one bit at a time), additional ports let you communicate with other IP, instantiated modules in the block design, and external ports, parameters let you reconfigure the IP while designing, so that you don't need to edit the IP again later (hopefully), and so that you can reuse it in different situations.

Link to comment
Share on other sites

An example of what I would do for this would be the two attached verilog source files. The attached image shows how to make the parameters visible in the customization GUI. In the IP Packager's Customization Parameters tab, double click on any parameter you want to be able to change from your block design and check the Visible in Customization GUI box.

The IP that these files would instantiate sets things up so that the following should always evaluate to true. (the IP just copies slv_reg0 onto slv_reg1's address).

Xil_Out32(BASEADDR, value);
newvalue = Xil_In32(BASEADDR+4);
return (value == newvalue);

The led and sw ports can be resized by customizing the IP in your block design. If you make those ports external in the block design and then properly constrain them, then Xil_Out32(BASEADDR+8, value); will set your boards leds, while Xil_In32(BASEADDR+12); will return the state of your boards switches.

myip_v1_0.v

myip_v1_0_S00_AXI.v

parameters.png

Link to comment
Share on other sites

@electronicsdevices

You need to map the new ports you have created in myiptest_v1_0_S00_AXI when you instantiate it in myiptest. Just like when you instantiate your MMultAXI4 module, you need to make sure that you map all of the different ports available. Take a look around line 64-84 of myiptest.v to see what I am talking about.

At a glance, this looks close.

Hope this helps,

Arthur

Link to comment
Share on other sites

@artvvb

1) What was the reason behind not using slv_reg1 in your design? Also, I noticed you did not comment out the "slv_reg1 <= 0;" part on line 218 or the "slv_reg3 <=0" on line 220 of the AXI file. Why?

2) What was the reason behind dumping the contents of slv_reg0 in the case of 2'h1 in addition to 2'h0 (i.e. why twice)?

3) It looks like you chose slv_reg2 for the LEDs. So that means I should be able to do something like Xil_Out32(BASEADDR+8, 0x00001001) if I want to light the 1st and 4th LEDs then right? Suppose I want to turn on the LED if I flip a switch. Could I replace the user logic with

assign led = sw;

so that I could avoid using the SDK to set it if I wanted to?

 

 

Link to comment
Share on other sites

1) slv_reg1 isn't being used because it's memory address is taken by the feedback from slv_reg0. I have that address configured as read only.

For line 218, good catch, I forgot, and didn't actually test this code, just knocked it together quickly to illustrate :).

2) As previously mentioned, this is intended to illustrate how to set up read-only and write-only registers. The assignment on line 369 is intended to show how to get data from PL to PS, the same as on line 371, but with the data flow coming from the processor instead of a port.

Lets assume that we treat each slave reg as a port of it's own, the code I posted was intended to map to something a little like the following pseudo code. I am thinking of the slv_reg# ports in this pseudocode block not as actual registers, but as what the processor will need to treat the associated addresses as.

module thing (
	//ports
    output [N-1:0] led,
    input  [M-1:0] sw,
    //implied bus to processor
    input [31:0] slv_reg0,
    output [31:0] slv_reg1,
    input [31:0] slv_reg2,
    output [31:0] slv_reg3
);
    parameter N=4;
    parameter M=4;
    assign led = slv_reg2;
    assign slv_reg3 = sw;
    assign slv_reg1 = slv_reg0;
endmodule

3) Absolutely, but if you aren't using SDK, there isn't much reason to include the AXI code. At that point you might just be better off with a pure-hdl project.

Something you could do that would be similar, but take advantage of the AXI bus would be:

assign led = sw | slv_reg2;

 

Link to comment
Share on other sites

@artvvb

I feel like I'm learning a lot, but there are still some things I'm unclear about:

1) On line 369, I see 2'h1. Is that the address that slv_reg1 was using? Why use two addresses for the same slv_reg0? Also, I don't understand what you did that makes it read only.

2) Sort of the same question. I'm still trying to make the connection as to how dumping the contents of slv_reg0 in the case of 2'h1 in addition to 2'h0 leads to setting up read-only and write-only registers

Link to comment
Share on other sites

@artvvb

Second attempt. I added signals to the already existing myiptest_v1_0_S00_AXI_inst and changed some names in myiptest of ports I got working in my original verilog design. The IP will synthesize, but I'm still not getting anything that is saying I need to specify a switch in the implementation phase. It is complaining about diff_clock_rtl_p not being implemented though.  MMultAXI4v remains unchanged.

myiptest_v1_0_S00_AXI.v

myiptest_v1_0.v

Link to comment
Share on other sites

2'h1 is the offset from the base address in terms of the registers, so it would be offset by 4 when accessing from SDK.

Where I think you are getting confused is in thinking about memory addresses as seen by SDK as "registers", which has a different meaning in the context of the HDL. SDK accesses the HDL code you define based on memory addresses, the HDL converts these addresses into values it can use to select which slave register to write to, or which piece of logic to read from. The process starting at line 213 defines how addresses are translated into registers when SDK writes values into the slave registers, so commenting out cases here defines which addresses cannot be written to. With the 2'h1 case commented out, calling Xil_Out32(BASE_ADDRESS+4, ...); will not have any effect on the values that your HDL sees. The process starting at line 363 defines how addresses are translated into what data will be read back into SDK.

The way to think about this is that 213 defines what and where Xil_Out32(base_address + 4*HDL_address, data); will write into the slave registers. 363 defines what data will be selected when you call Xil_In32(base_address + 4*HDL_address);.

With this in mind, the address represented by the read 2'h1 case selects the data previously written into slv_reg0 by the write 2'h0 case.

Does that help?

 

Link to comment
Share on other sites

There is a key point:

you don't have to comment any slv_reg#

After exporting hardware to SDK Launch on hardware(GDB) is not enough.

You have to program FPGA, then  Launch on hardware (System debugger) instead of (GDB) .

Link to comment
Share on other sites

@artvvb

All I really did is put mytestip and Microblaze on it. 59836781b3ddc_Screenshotfrom2017-08-0313-09-36.thumb.png.cd59680564271307f42a24f1f9764a4a.png

I'm wondering why it isn't complaining that I haven't assigned a switch to A,B,Z, and MMult_AB.

That's kinda what I don't get about it. I'm making a hybrid model of sorts. If instead I write it with just verilog, then when I go to implement the device, it asks me what switch I want to use (I didn't specify an .xdc file intentionally). In the past, if I wanted to use a switch, I could do that by adding the switch IP to the block design. I'm not really sure what to do if I want to combine the approaches.

Link to comment
Share on other sites

@D@n

From reading your website, you seem to be a fan of simulation:

On 8/1/2017 at 3:28 PM, D@n said:

 

This leads me to a couple of thoughts:

  1. Simulate your design first, before running on the hardware.  I have found and love Verilator as a tool, and I've written a short tutorial on how to use it.

 

So I decided to simulate my design (even though I know it already works). I wrote the testbench and simulated it. What I want to happen is this: when the MMult_AB and Z switches are turned on (and A and B are off), the FPGA should multiply matrices A and B, check to see if the results are correct, and light an LED if they are. What the simulation says however, is that once MMult_AB and Z are both on, the LED automatically lights up (i.e. no delay). Of course, there must be some sort of delay, but this is not shown in the simulation. So how do you measure the delay?

59837c45b8a22_Screenshotfrom2017-08-0314-27-10.thumb.png.7658fa83277077bd708bc699837a5d7f.png

MMultAB_tb.v

MMultAB.v

Link to comment
Share on other sites

@electronicsdevices,

Gosh, look what your simulation just revealed ... (I reviewed MMultAB.v only, not MMultAB_tb.v)

  1. Your design (MMultAB.v) takes no time at all, since none of the transitions take place on the positive edge of any clock.
  2. The synthesizer did all your logic for you, and only programmed the result into the resulting registers.  No operations actually took place.  A simple set of 4-input LUTs (A, B, C, and MultAB) can be used to select among four apriori known answers.  It's a memory lookup to the answer, rather than any computations taking place.

So, let me ask, are you sure your design actually does what you want it to?  And, since you hadn't simulated your design, you really only had a false sense of what your design was doing up until now.

So, yeah, I am a big fan of simulation.  This is a wonderful example of why.

Were I to redo this, I'd rebuild the module so that the FPGA inputs could be set externally.  That would keep the synthesizer from optimizing away your logic.  It would also fail timing, forcing you to put the clocks back in that you need.  As for setting it externally, may I recommend using a debugging bus?  I like to use that to read/write an FPGA's data from software.  I consider the debugging bus interface to be more reliable than buttons and switches, but you may need to read the next several blog posts to know why.  (Or read the wikipedia paragraph on contact bouncing, and take a look at what buttons can do that you aren't expecting here)

Dan

Link to comment
Share on other sites

@electronicsdevices

And look at what your block design revealed, your ip should have additional ports for the inputs and outputs in your "Stuff I Added" section of myiptest_v1_0.

Have you merged file changes into the IP packager and re-packaged your IP since you added these ports? After doing so you will need to Refresh your IP repo and Upgrade the myiptest IP. "Refresh" should show up in a pop-up after you re-package, and you can do the Upgrade by doing Tools -> Reports -> Report IP Status, and then clicking Upgrade All.

Link to comment
Share on other sites

@D@n

So for whatever reason, the matrix multiplication from the prior version would return the zero matrix in simulation. So I decided to scrap the design and start over.

I started off with the most basic part (the matrix multiplication) and got that working. I didn't hardcode the values like I did before. I put them in the testbench instead.

When I ran the simulation, I get the correct result, but again, no delay.

5988e9f0a579b_Screenshotfrom2017-08-0717-23-47.thumb.png.f6d22c8ed010c4317ae3d5417bdb58aa.png

 

Is it even possible to get some sort of delay in a simulation without using physical parts? You mentioned clocks. I tried that with a prior simulation, but I still got no delay

5988e99d3e44f_Screenshotfrom2017-08-0314-27-10.thumb.png.f7f4ed4bc66ee4f33eccac851f4d40ec.png

 

I also tried your debugging bus and ran into some issues as I followed your tutorial:

http://zipcpu.com/blog/2017/06/29/sw-dbg-interface.html

The big one is that there is no makefile in ~/filepath/dbgbus. I ended up compiling each individual makefile and got it to work, but still, what you say in the tutorial doesn't work when you try it.

5988eb8fa3525_Screenshotfrom2017-08-0716-56-45.thumb.png.1ec782a0a2f7de913be6554c24f2cd4e.png

 

I also tried writing to the BRAM named "MEM," but that register wasn't recognized. I think it's because earlier on in the tutorial there was source code I need to have written prior to testing it.

5988ebd535455_Screenshotfrom2017-08-0717-13-52.thumb.png.52bc25e3133809b60285787a8107e37f.png

 

How do I build a design where I can get some sort of delay that would be comparable to real life? The issue seems to be the compiler makes it so the design doesn't need to do the calculation, so you mentioned externally feeding the FPGA values, but how is that possible in a simulation? I can do it with Vivado SDK easily, but that's in realtime. The simulation is important, but how can I get reasonable data if the compiler is preventing the very behavior I want to examine?

Verilog files are attached.

 

 

Mult_AB_tb.v

Mult_AB.v

Link to comment
Share on other sites

@electronicsdevices,

Your design takes no time at all, because that was what you told it to do.

If you want logic to take place "instantaneously" in a simulator, use an "assign" statement, or use an always statement that doesn't depend upon a clock.  "always @(posedge clk)" will require that the logic take a clock to take place.  Further, your test bench file itself will need to "create" the clock.  A little google turned up this article which discusses how to make a clock within your testbench. 

The simulator, though, doesn't match reality and in reality all logic takes time.  Your goal as a designer is to make sure that the time required by your logic is a touch less than the time between clock ticks.  Multiplies and adds take time.  I'm going to wager that "a0*b0+a1*b2" takes 2 DSP's and 20ns to do.  (It could be as fast as 10ns ... might be worth measuring.) 

Thank you for pointing out the issues with the debugging bus.  I think I've now fixed all of them.  Try going into the debugging bus directory, running "git pull" and then "make" from the main directory.  There's now a makefile there.  I also fixed the oversight of not having a register named "MEM" in regdefs.cpp.  That's now been fixed.

As for a design with a delay that would be comparable to real life ... I've only ever managed to do that by working back and forth with Vivado to implement my design.  Once my design implements, I know the delays within it are close to real life.  Use the clock, though ... Vivado is going to measure your design against the constraint that logic created on one clock must be completed before the next clock.  Vivado can only make that measurement when your logic starts on one clock and has to complete by the next.

Is it possible to externally feed an FPGA values in a simulation?  Yes.  I do it all the time.  The dbgbus simulation is doing that with the UART (it's just a C++ program ...).  However, that simulation isn't connected to the amount of time an operation requires.  To get that, I both simulate and build my design.  When building it, I check that it meets any timing requirements, etc.

Dan

Link to comment
Share on other sites

On ‎08‎/‎11‎/‎2017 at 1:54 AM, artvvb said:

Lets assume that we treat each slave reg as a port of it's own, the code I posted was intended to map to something a little like the following pseudo code. I am thinking of the slv_reg# ports in this pseudocode block not as actual registers, but as what the processor will need to treat the associated addresses as.

 

 

Dear

I'm newbie to AXI core. I have reviewed all your replies and I have learned lots of things.

Actually I'm puzzled when I want to add some Verilog code under "Add User Logic here" section.

for example I  set slv_reg0 with a number in the SDK. (I have used Xil_Out32(add,0x2311))

the first 16 bits of slv_reg0 is dedicated to the Hours

and second 16 bits of slv_reg0 is dedicated to the Minuets

after that I will convert the HH and MM to a proper 7 seg code. (using a lookup)

now how should I write a module under "Add User Logic here"?

Actually in pure Verilog coding it's easy but here I'm very puzzled!

could you please help me? please.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...