My prior post regarding AutoFPGA discussed why I built AutoFPGA and what I intended to accomplish with it.

Today, let’s take a look at how to create some simple AutoFPGA component files that you can then use within a basic AutoFPGA design. Specifically, let’s look at how to connect wishbone components having only a single data register to an AutoFPGA project.

To do this, we’ll start out by taking a look at AutoFPGA’s configuration file format. You’ll need that to understand the basics of how to read the configuration files themselves. We’ll then discuss briefly the two Verilog files AutoFPGA generates, toplevel.v and main.v, and then how the various AutoFPGA tags get placed within that structure. This will then lead us to the point where we can create the AutoFPGA configuration files necessary to read and write single registers from a peripheral.

AutoFPGA’s Configuration File Format

Before we start discussing the details of how to write particular AutoFPGA cofiguration files, it only makes sense to pause to comment on the file structure in general. Indeed, AutoFPGA’s configuration file structure is actually fairly simple, with its focus primarily on staying out of the way.

Key value pairs

AutoFPGA configuration files consist of a series key value pairs. Keys start at the beginning of a line with an @ sign and end with an = sign. Hence, to define the key KEY to have a value VALUE, you’d write:

@KEY=VALUE

If the VALUE fits within a single line, spaces will be trimmed from the ends of it.

Multi-line values

Value’s can also take up multiple lines, as in,

@KEY=
/* The keys value will be given
 * by these series of lines.
 */

Any white space found within multiple line VALUEs will be left alone.

Comments

AutoFPGA allows line based comments. Comment lines begin with either a # and a space, or two ##s. Other comment characters, such as // or /* ... */ will create comments within the result files that the VALUE gets pasted into.

# This is a comment within AutoFPGA
##This is also a comment
@KEY=
/* This comment will be copy/pasted into the source file this KEY's VALUE
 * will be pasted into.
 */
// This comment will also be copy/pasted as part of AutoFPGA's work

Unordered

Keys within an AutoFPGA configuration file are unordered, with the only exception being the @PREFIX key that separates components within a given configuration file (more on this key in a moment). What this means is that you can place keys for the top level module before those for the main module, after the main module keys, or interspersed between the two.

Substitution

VALUEs from one KEY may be substituted into VALUEs from another, by referencing the value by its keys name, as in @$(KEY).

For example, if you define a key @DEVID,

@DEVID=VERSION

You can later reference this key within another key’s value by using @$(DEVID to reference it. Hence,

@REGS.0= 0 R_@$(DEVID) $(DEVID)

will get expanded into

@REGS.0= 0 R_VERSION VERSION

Expressions

KEYs that start with @$ instead of @ define integer valued expressions. AutoFPGA contains a simple expression evaluator, allowing things like:

@$BAUDCLOCKS=@$(BAUDRATE)/@$(CLKFREQHZ)

In this case, if you’ve defined BAUDRATE as

@BAUDRATE=1000000

or even

@$BAUDRATE=1000000

and CLKFREQHZ as

@$CLKFREQHZ=100000000

then

AutoFPGA would calculate the @BAUDCLOCKS frequency to be 100 clocks. If @$(BAUDCLOCKS) is used in an string context, it will evaluate to the string "100". (The format is adjustable via the @BAUDCLOCKS.FORMAT tag, but we’ll get to that later.)

The @PREFIX key

All AutoFPGA components begin with a @PREFIX key. This key defines both the beginning of the component’s keys within a configuration file, as well as the end of any prior component that may also be defined within the file. The @PREFIX tag also creates a sort of local variable namespace, since keys defined following a @PREFIX key are quietly prefixed in the global key structure by the @PREFIX.

While there are a couple of other details to AutoFPGA’s configuration files, those other details can wait until we need them later.

AutoFPGA’s RTL File Structure

Before you integrate your component into AutoFPGA, you’ll wnat to understand how AutoFPGA looks at RTL (Verilog) files.

In general, AutoFPGA insists that a design should have the structure shown in Fig 1. In this structure, the AutoFPGA generated files are marked with a red star.

Fig 1: AutoFPGA RTL Structure
AutoFPGA RTL File Structure

In this structure, there is a top level file. Within this top level file are any references to vendor specific components. For example, MMCMs, PLLs, ISERDESE2 and OSERDESE2 components would be placed either in the top level itself, or in the vendor specific files beneath it. As an example, the Xilinx Memory Interface Generator (MIG) code is vendor specific, so it would belong at this top level. Another example might be the vendor specific components necessary for a HDMI receiver. The actual line of demarkation is made by examining what Verilator can simulate. Any components that Verilator can components can be placed in main.v and below, anything else goes into the top level or the vendor specific component set that it references.

Critical components of your design that don’t meet the criteria to be placed into main.v may need external (C++ software) co-simulators. We’ll come back to the concept of simulation later.

Also within the top level file is an instantiation of the main module. The main module consists of one (or more) bus masters controlling a set of peripherals. It can also contain items that are neither bus masters nor peripherals, but these items won’t get attached to any bus.

AutoFPGA will build both the top level module, as well as the main module for you–connecting components together as the need to be for your design.

However, AutoFPGA has no knowledge of the vendor specific or vendor independent component files. All it knows how to do is to copy and paste items into these two files to create your logic. You tell it how to interface with the other components, but that’s it.

Well, this isn’t quite true. AutoFPGA supports some Makefile based tags which can be used to create a make include file. You can use these tags to place a list of component items into a list of Verilog files included in your project. Some synthesizers, such as yosys need this information in order to build the project. Other synthesizers, isuch as Verilator, just need to be told where to look. We’ll come back to this topic later.

What I”m trying to say is that after AutoFPGA has created any top level file or main module, you’ll still need to synthesize your design from these module files. AutoFPGA doesn’t create make files, and it doesn’t build projects. These capabilities need to be part of the project that uses AutoFPGA.

Internal RTL File Structure

AutoFPGA is primarily a copy and paste utility, with some additional capabilities for composing and connecting bus structures and interrupts. What this means when it comes to the two RTL files that it generates is that AutoFPGA will primarily copy snippets of Verilog code, provided by your configuration file, into their proper places within these two files.

The majority of this work is done within main.v so let’s look at that first.

Fig 2: Main module structure
main.v structure

Fig 2 shows the structure of the main module.

The main module, as shown in Fig 2, has several parts to it. It starts like all AutoFPGA generated file with a legal notice, copied from the file whose name is associated with the @LEGAL key. After that, there’s a section where components can define ACCESS keys. These are pre-processor macros, that will be used to handle component dependencies if necessary (@DEPENDS key). Then comes a series of paste’d components from any @MAIN.PORTLIST keys, then any parameter definitions captured in @MAIN.PARAM keys, and then any declarations associated with @MAIN.IODECL keys. The file continues, defining any interrupt wires, wires defined within @MAIN.DEFNS tags, interrupt vectors, and bus interaction wire components. The big point to know here is that these keys are pasted into this file in the order shown. I’ve written the keys in all capitols in the positions where their values will be pasted in within Fig 2, so you can see what goes where.

The primary module for the component, where the actual bus interaction and any I/O wire adjustment takes place, is placed within the value of the @MAIN.INSERT tag for the component. We’ll discuss many of the other details as we go along.

Fig 3: Toplevel file structure
toplevel.v structure

The top-level module has a similar, although simpler, structure as shown in Fig 3.

Unlike the main module, fewer KEYs impact the top-level. There’s the list of I/O ports for the module, their declarations, and any other definitions you might wish to have. After that, there’s the KEY defining wires that need to be passed to the main module, @TOP.MAIN, and any RTL code that needs to be inserted into the top-level. module via the @TOP.INSERT key.

Not all components will need these top-level keys. If no top-level keys are given, then any keys describing ports for the main.v, file will automatically be assumed to be toplevel, ports as well, so they need not be defined twice if they are the same between both levels.

So, let’s take a look at what it takes to put a configuration file together that will specify how to impact the top level, or main module. We’ll start with some simple examples, and then move to some more complex examples.

Remember, as you go through these, that AutoFPGA is primarily a copy/paste utility, with some bus aggregation facilities added into it.

Reading a simple ad-hoc value

For our first example, let’s read a constant value from our bus. The value itself is arbitrary for this example, so we’ll just set it to 32'h20170926. You can see the full configuration file for this example here.

The first step is to declare this item as a peripheral–as its own entity, and to give it a name that will distinguish it from other peripherals within our design. This is done via the @PREFIX key. Let’s give this fixed data component the name fixdata, since that’s what we’ll be reading from it: fixed data.

@PREFIX=fixdata

The @PREFIX key is required for all components. It defines the beginning of the component, and ends any component definitions prior to it. (Yes, two components can be placed into a single configuration file. A good example of this would be the flash component that has not only read-only flash memory, but also a set of program control registers to control the erase circuitry, and any write-enable.)

We may also want an all-caps name for some of the contexts this peripheral might be in. This isn’t required, like the @PREFIX key is, but it makes a nice illustration. For this, we’ll create the key DEVID, and give it a value of FIXEDATA.

@DEVID=FIXEDATA

This is a classic example of a key that AutoFPGA knows nothing about. It will have only the meaning we give it below.

If you want this peripheral to have a place on the bus, then AutoFPGA needs to know how many addresses to assign to it. Let’s take just one bus address (four bytes for a 32-bit data bus–the default), since we’re only going to be returning a single, pre-determined, value.

@NADDR=1

Most of the example’s we’ll deal with today have only one address.

This particular peripheral is going to be a slave peripheral to the main wishbone bus, wb within our design.

@SLAVE.BUS=wb

Once a component is declared to be a bus slave, a series of wires will be defined for it within main.v as referenced in Fig 2 above. For our component, these wires are given by,

wire	[31:0]	fixdata_data;
wire		fixdata_ack, fixdata_stall, fixdata_sel;

although I’ll tend to reference these as @$(PREFIX)_data, @$(PREFIX)_ack, etc., since these descriptions are more generic and can be used regardless of what the peripherals name, given by the @PREFIX key, actually is. The first three of these are the slave return wires required by the wishbone bus. The third wire, @$(PREFIX)_sel will be true any time the address on the bus references this component.

Because our value is already fixed, it won’t take any clocks to calculate it–it’s already known. Therefore this slave is of type SINGLE, since it can return a result in a SINGLE clock cycle. SINGLE peripherals don’t need to define their wishbone acknowledgement wires, @$(PREFIX)_ack, or their stall lines, @$(PREFIX)_stall, since they never stall and the ack is true the same clock the peripheral is accessed.

@SLAVE.TYPE=SINGLE

Other peripheral types are DOUBLE, OTHER, and MEMORY. DOUBLE peripherals require a single clock to calculate their value, yet never stall. OTHER and MEMORY peripherals are more generic wishbone peripherals–but we’ll discuss these more later.

It’s now time to return our value to the bus.

@MAIN.INSERT=
	assign	@$(PREFIX)_data = 32'h20170926;

Normally a wishbone slave also needs to assign values to a @$(PREFIX)_ack and @$(PREFIX)_stall line. However, these are trivially defined for a SINGLE peripheral type, so we ignore them here. (The AutoFPGA generated main.v file ignores these lines for SINGLE type peripherals as well.)

The last step in our peripheral definition is to give a name for this peripheral to be used when accessing it via the debugging interface. This is where we’ll use the @DEVID key we defined earlier. The first of the lines below just specifies that this peripheral has only one named register. The second line says that, at an offset of zero words from the beginning of this peripheral’s address is a register named R_@$(DEVID) with a user name of @$(DEVID). These will be turned into R_FIXEDATA and FIXEDATA respectively.

@REGS.N=1
@REGS.0= 0 R_@$(DEVID) @$(DEVID)

If you look in regdefs.h, or regdefs.cpp, for R_FIXEDATA, you’ll see the effects of these two lines.

Fig 4: Software interface files and AutoFPGA
Diagram showing that AutoFPGA creates regdefs.h and regdefs.cpp

While we haven’t discussed it, these two files, regdefs.h, and regdefs.cpp, are part of the host-based software interface library specification shown in Fig 4. They are marked with red stars, since they are created by AutoFPGA, vice the design.h file created by the RTL Makefile.

Once we include this fixdata.txt configuration file in our design, and build it, we can then run wbregs

wbregs FIXEDATA

to read from the peripheral–either in simulation or on our actual hardware.

Reading a Fixed Version number

What if we want our number to be defined by an include file?

Many of my designs contain an include file, builddate.v, that is created by a simple perl script. This include file defines a value DATESTAMP containing the date when mkdatev.pl was last run. I use this as a version number within my design–primarily to make certain I’m not accidentally running an older design when I think I’ve built and loaded a newer design.

Creating a register containing this version information is almost identical to the last exercise. First, we declare our bus interface as before, although this time with a different @PREFIX and @DEVID.

@PREFIX=version
@DEVID=VERSION
@NADDR=1
@SLAVE.TYPE=SINGLE
@SLAVE.BUS=wb

At this point, though, we want to instruct our main module to include the Perl-script generated build-date version file. This is done through the @MAIN.DEFNS key, whose value then gets pasted at the top of the main module.

@MAIN.DEFNS=
`include "builddate.v"

Remember, AutoFPGA is primarily a copy and paste utility. You can place any Verilog you want into your main module file at this same location using this tag, although not all things you could paste would make sense at the top of a module file and before the module declaration.

Our bus interaction code within the main module, given by the @MAIN.INSERT key, is going to change only slightly. This time, instead of returning 32'h20170926, we’ll return our DATESTAMP variable.

@MAIN.INSERT=
	assign	@$(PREFIX)_data = `DATESTAMP;

The rest is just like the last example.

@REGS.N=1
@REGS.0= 0 R_@$(DEVID) @$(DEVID)

You can see the full configuration file for reading this version information here.

One thing to note is that AutoFPGA only creates some files. It doesn’t create an entire project. Hence, the creation of the builddate.v file is done by the main Makefile calling the mkdatev.pl program–both of which are outside of the configuration files that AutoFPGA looks at and creates.

Read access to an internal (changing) register

Let’s create another single register within our design using AutoFPGA. Suppose we wanted to know how many clock ticks had taken place since we first initialized our design, and whether the count had (eventually) rolled over. To get this value, we’d need to initialize a register and then count clocks with it.

The pwrcount.txt configuration file describes just how to build one of such a register. We’ll walk through this file below.

Since this is a single value, again, the bus access declaration is the same as before but with a new name, pwrcount.

@PREFIX=pwrcount
@NADDR=1
@SLAVE.TYPE=SINGLE
@SLAVE.BUS=wb
@REGS.N=1
@REGS.0= 0 R_PWRCOUNT PWRCOUNT

Note that I didn’t use the DEVID this time. AutoFPGA doesn’t know that key, and so it won’t miss it when it isn’t there. It’s just an example of a key you can use if it helps you, or ignore if not.

At this point, I could just copy and paste a module reference into my main module,

@MAIN.INSERT=
	powercounter @$(PREFIX)i(i_clk, @$(PREFIX)_data);

assuming I had a module named powercounter. We’ll use that approach with a later example in this post for the special purpose I/O peripheral, spio.v,

Instead of doing things that way, though, let’s define a register, r_pwrcount_data. This register declaration will get placed at the top of the main module, before any logic, so that it can be referenced by any logic below. Before doing so, though, we’ll replace pwrcount within that name by our PREFIX key, so as to continue the perception of variables with a local scope.

@MAIN.DEFNS=
	reg	[31:0]	r_@$(PREFIX)_data;

Now we can create the logic that defines this value.

@MAIN.INSERT=
	initial	r_@$(PREFIX)_data = 32'h0;
	always @(posedge i_clk)
	if (r_@$(PREFIX)_data[31])
		r_@$(PREFIX)_data[30:0] <= r_@$(PREFIX)_data[30:0] + 1'b1;
	else
		r_@$(PREFIX)_data[31:0] <= r_@$(PREFIX)_data[31:0] + 1'b1;
	assign	@$(PREFIX)_data = r_@$(PREFIX)_data;

Note that when we we are done, we set @$(PREFIX)_data using an assign statement rather than using r_@$(PREFIX)_data. This is simply because AutoFPGA defines the bus variables for you as though you were going to create and return them via a module–as wires, not registers.

Getting the address of the last bus error

In a similar fashion, we can place just about any logic we want within the @MAIN.INSERT tag. Remember, AutoFPGA is primarily a copy and paste utility. All you need to do is to tell it how to wire your code up to the rest of the main module.

@PREFIX=buserr
# ...
@MAIN.DEFNS=
	reg	[@$(SLAVE.BUS.AWID)-1:0]	r_@$(PREFIX)_addr;
@MAIN.INSERT=
	always @(posedge @$(SLAVE.BUS.CLOCK.WIRE))
		if (@$(SLAVE.BUS.NAME)_err)
			r_@$(PREFIX)_addr <= @$(SLAVE.BUS.NAME)_addr;
	assign	@$(PREFIX)_data = { {(@$(SLAVE.BUS.WIDTH)-2-@$(SLAVE.BUS.AWID)){1'b0}},
			r_@$(PREFIX)_addr, 2'b00 };

This works based upon the fact that there are several wires defined for each bus. These wires are all prefixed by the name of the bus and an underscore. Hence, for the wb bus, these wires will all have a wb_ prefix. Since this is a wishbone B4 pipelined bus, all of these bus master wires will be defined as:

wire			wb_cyc, wb_stb, wb_we, wb_err, wb_stall,
			wb_none_sel;
reg			wb_many_ack;
wire	[(AW-1):0]	wb_addr;
wire	[(DW-1):0]	wb_data;
reg	[(DW-1):0]	wb_idata;
wire	[(DW/8-1):0]	wb_sel;
reg			wb_ack;

where AW is the address width of the bus, and DW is the data width. AutoFPGA needs to be able to support multiple busses, however, of multiple types. For this reason, the @SLAVE.BUS key is expanded into a series of keys:

@SLAVE.BUS.NAME=wb
@SLAVE.BUS.AWID=16
@SLAVE.BUS.WIDTH=32
@SLAVE.BUS.TYPE=wb
@SLAVE.BUS.CLOCK.NAME=clk
@SLAVE.BUS.CLOCK.WIRE=i_clk
@SLAVE.BUS.CLOCK.FREQUENCY=1000000

This allows us to reference the bus clock using @$(SLAVE.BUS.CLOCK.WIRE), or even the wb prefix of the bus as @$(SLAVE.BUS.NAME). This means that the address width of this slave is given by @$(SLAVE.BUS.AWID) and the bus data width is given by @$(SLAVE.BUS.WIDTH).

If you find these long key names tedious, you can rename them within your component. For example, we could have created a key @CLK and assigned it to hold the value of @$(SLAVE.BUS.CLOCK.WIRE), another key @AW and assigned it to hold the value of @$(SLAVE.BUS.AWID), and other keys @BS and @DW as in,

@CLK= @$(SLAVE.BUS.CLOCK.WIRE)
@AW= @$(SLAVE.BUS.AWID)
@BS= @$(SLAVE.BUS.NAME)
@DW= @$(SLAVE.BUS.WIDTH)

Had we done so, our @MAIN.DEFNS and @MAIN.INSERT tag might have been simplified to

@MAIN.DEFNS=
	reg	[@$(AW)-1:0]	r_@$(PREFIX)_addr;
@MAIN.INSERT=
	always @(posedge @$(CLK))
		if (@$(BN)_err)
			r_@$(PREFIX)_addr <= @$(BN)_addr;
	assign	@$(PREFIX)_data = { {(@$(DW)-2-@$(AW)){1'b0}},
			r_@$(PREFIX)_addr, 2'b00 };

What this particular configuration file does different from our previous examples is to reference information about the bus using the keys defined for any bus. This allows the bus error address register, defined above, to only have as many bits defined as necessary (one for each address line, or @$(AW)). Not only will this register size be adjusted if the width of the address bus is adjusted, but it will also be adjusted if the width of the data bus is adjusted–as illustrated by the @$(PREFIX)_data’s dependence upon both @$(DW) and @$(AW). Had we worked a little harder, we might’ve also set up the trailing two zero bits to expand as necessary with any changing data width.

Write support for the same value

Now that you know how to read a value from the bus, what about writing to registers on the bus?

The raw register configuration file shows how this might be done. This example peripheral does nothing but set a value you give to it upon any write, and then allow you to read that value back later.

In many ways, this peripheral is almost identical to the power counter component we discussed earlier. The difference is that this register can be set via a bus write. For this reason, the boiler plate setup is almost identical.

@PREFIX=rawreg
@DEVID=RAWREG
@NADDR=1
@SLAVE.TYPE=SINGLE
@SLAVE.BUS=wb
@REGS.N=1
@REGS.0= 0 R_@$(DEVID) @$(DEVID)
@MAIN.DEFNS=
	reg	[31:0]	r_@$(PREFIX)_data;

The difference is found within the @MAIN.INSERT tag describing how this peripheral deals with the bus. In this case, for any bus access, wb_stb, that is also a write, wb_we, and that references this peripheral, @$(PREFIX)_sel, we update our register. (Remember how a generic wishbone slave interacts with the bus?)

@MAIN.INSERT=
	initial	r_@$(PREFIX)_data = 32'h0;
	always @(posedge i_clk)
		if ((wb_stb)&&(wb_we)&&(@$(PREFIX)_sel))
			r_@$(PREFIX)_data <= wb_data;
	assign	@$(PREFIX)_data = r_@$(PREFIX)_data;

We could have made this module more generic by replacing the wb prefixes with @$(SLAVE.BUS.NAME). Had we done that, we would have then been able to use this across busses with different names. For now, because of how we’ve defined this, this component will always only ever read it’s values from the bus with a wb_ prefix.

Reading values from external ports

Let’s look at one more example along this same theme. Suppose we wanted to add to our design a register allowing us to:

  1. Read from the buttons in our design

  2. Read from any switches on our board

  3. Set any output LEDs

Unlike our previous examples, this example will require access to I/O ports within our design, so we’ll have to deal with this when we get there.

We’ll start with the keys we’ve already defined above.

@PREFIX=spio
@NADDR=1
@DEVID=SPIO
@SLAVE.TYPE=SINGLE
@SLAVE.BUS=wb
@REGS.N=1
@REGS.0= 0 R_@$(DEVID)		@$(DEVID)

Now let’s walk through the new keys.

The first of these new keys is an @ACCESS key. It’s an optional key, as you can tell from the fact none of our other components above had this key.

@ACCESS=SPIO_ACCESS

This @ACCESS key, if present, will create a define line at the beginning of our main.v file.

`define	SPIO_ACCESS

The RTL Makefile then includes instructions to copy this line from the main.v file into a design.h file, allowing any associated C++ files to know whether or not this component is a part of our design.

The next new key is the @MAIN.PORTLIST key. This key specifies values to be placed at the top of main.v within the portlist for the main module.

@MAIN.PORTLIST=
		// @$(DEVID) interface
		i_sw, i_btnc, i_btnd, i_btnl, i_btnr, i_btnu, o_led

This key is unique in that the items within it are separated by commas, but that the last item has no comma following it. This allows AutoFPGA to put a comma between items, or not, as is necessary to compose multiple component I/Os into a design.

Immediately following the main module declaration is an opportunity to place parameters. Let’s identify that this particular design has five buttons, 8 LEDs, and 8 switches.

@MAIN.PARAM=
	// @$(DEVID) interface
	localparam	NBTN=5,
			NLEDS=8,
			NSW=8;

Following any @MAIN.PARAM values, you can declare your I/O values.

@MAIN.IODECL=
	input	wire	[(NSW-1):0]	i_sw;
	input	wire		i_btnc, i_btnd, i_btnl, i_btnr, i_btnu;
	output	wire	[(NLEDS-1):0]	o_led;

As with so many other keys, these values will simply be pasted into the main.v file following any key-value substitutions.

In a like manner to the way we’ve defined variables before, we’ll define a five valued logic vector to hold the inputs from each of our button values.

@MAIN.DEFNS=
	wire	[(NBTN-1):0]	w_btn;

This will get placed with all of the wire and register declarations at the top of the main.v file.

The next required piece is the @MAIN.INSERT key, telling AutoFPGA how we are going to interact with the bus. In this case, we’ll compose our five buttons into a quick vector, and then reference a special purpose I/O module I call spio.v. This module handles bus writes and bus reads, and is a generic wishbone peripheral.

@MAIN.INSERT=
	assign	w_btn = { i_btnc, i_btnd, i_btnl, i_btnr, i_btnu };

	spio #(.NBTN(NBTN), .NLEDS(NLEDS), .NSW(NSW)) @$(PREFIX)i(i_clk,
		wb_cyc, (wb_stb)&&(@$(PREFIX)_sel), wb_we, wb_data, wb_sel,
			@$(PREFIX)_ack, @$(PREFIX)_stall, @$(PREFIX)_data,
		i_sw, w_btn, o_led, spio_int);
@MAIN.ALT=
	assign	w_btn    = 0;
	assign	o_led    = 0;

Since this is a generic peripheral, it needs to return @$(PREFIX)_ack and @$(PREFIX)_stall values–even though we’ll ignore them since this is peripheral has a SINGLE bus interaction type.

The new key that we haven’t discussed yet is the @MAIN.ALT key. This key defines the logic that will be used in the case the @ACCESS component defining SPIO_ACCESS is not defined. Items within this keyword are used for defining values external ports, such as o_led, should be given should the primary logic for this component not be used.

This component contains one last tag, @INT.SPIO.WIRE.

@INT.SPIO.WIRE=spio_int

This tag identifies that spio_int is a single wire, and that it may be assigned to an interrupt vector as the interrupt generated by this peripheral.

Feel free to take a look at the AutoFPGA generated main.v file and see how we did!

More to come!

The goal of AutoFPGA was to be able to have a simple command line interface–simply run AutoFPGA with a list of peripherals, and get output files. You might wish to test this by removing any of the above files from the AutoFPGA command line to see how well we did, or even add some configuration files of your own.

If you choose to try this, you’ll notice that the AutoFPGA demo project contains more configuration files than the ones we’ve looked at today. These are the block RAM configuration file, the clock file that defines the clock and the reset wires, the debugging bus configuration file, and a global configuration file. Of these, only the block RAM configuration file may be easily removed. The debugging bus configuration file defines our bus master, and a bus doesn’t work very well without a master, so it would make more sense to replace that one than to remove it if you wish to change it. The clock file and global configuration file are not as easily replaced.

So, let’s come back and discuss the block RAM configuration file at a later time. Specifically, you’ll love how easy it is to change the size of the block RAM, and how well that size information is propagated throughout a AutoFPGA-based design.

We’ll also need to come back later to discuss how a wishbone bus master can be connected using AutoFPGA.

Until that time, our demonstration project plus this article should be enough to build and control most beginning FPGA tasks. Even better, and unlike most proprietary solutions, all of the code AutoFPGA builds is availble for your inspection and learning pleasure.