Updating the First Stage Bootloader in Petalinux v2017.4


I am developing a prototype system that uses a lot (9) ZC706 boards. I need each board to boot with a different Ethernet MAC and IP address. I wanted this to be configured using the four DIP switches on the board.


To do this you need to modify the first stage bootloader (FSBL) to read the dip switch values and then pass the result to U-Boot. There is no built-in mechanism to do this so you need to modify U-Boot as well.

The plan is to modify the fsbl_hooks.c file in the FSBL to read the DIP switch value. Then it will place a message in the on-chip RAM at address 0xfffffc00. This location is unused by U-Boot. We then get U-Boot to look at the message to set the Ethernet address. Simple? Not hardly.

Modify fsbl_hooks.c

I’m not going to go over the FPGA logic for this change. It’s really pretty simple. Hook up the dip switch inputs to a GPIO unit in the PL. See my tutorials if you don’t already know how to do this. Next we modify the FsblHookBeforeHandoff function. First we need to read the DIP switch value. My GPIO block is at address 0x41200000 and I wired the switches into bits 8-11. So we first set those bits to inputs by writing 0xf00 to address 0x41200004 and then we read the dip switch values from address 0x41200000. We need to shift the result right by 8 to align the switch input values.

Next we create a string for U-Boot to consume. This is in the form of a command setting the U-Boot ethaddr variable to the desired MAC address.

u32 FsblHookBeforeHandoff(void)
	u32 Status;
	u32 dip_sw;
	int i;
	char ethaddr[40];

	Status = XST_SUCCESS;

	 * User logic to be added here.
	 * Errors to be stored in the status variable and returned
	Xil_Out32(0x41200004,0xf00); /* Set DIP switch as inputs */
	dip_sw = Xil_In32(0x41200000)>>8; /* Read values */

	fsbl_printf(DEBUG_INFO,"In FsblHookBeforeHandoff function \r\n");
	for (i=0; i<strlen(ethaddr)+1; i++)

	return (Status);

Modify U-Boot

Next we need to get U-Boot to pay attention to the message. To do this we need to add the following to the project-name/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h file.

#define CONFIG_PREBOOT	"echo U-BOOT for ${hostname};setenv preboot; echo;env import -t 0xFFFFFC00"

The trick is the env import -t 0xFFFFFCOO command. This tells U-Boot to import the string that the FSBL conveniently placed at that address.

That’s not so hard…

Great. That’s all we need to do right? Child, you’ve got a thing or two to learn. So far things have been easy. But even though petalinux-create creates the file components/plnx_workspace/fsbl/fsbl/src/fsbl_hooks.c for you to modify, it actually ignores that file completely. Likewise you could make a copy of the ZC706 BSP file and change the copy in there and then use your modified BSP file. Hah, amateur. That file is also ignored. You can modify the file in the Petalinux install directory but then all your Petalinux builds will get this little change. Clearly not desirable

What we need here is a patch file. We need to place a patch file in the components/plnx_workspace/fsbl/fsbl/src directory and then make a bbappend file that Petalinux will use to patch the fsbl_hooks.c file after it obtains the original file from god knows where but before it builds the FSBL.

Making the FSBL patch file

In order to build the patch you first need to check out the Xilinx embedded software git repository.

git clone https://github.com/Xilinx/embeddedsw.git

Now you need to checkout the branch for the version of Petalinux you are interested in. Here I’m using 2017.4.

$ cd embeddedsw/
$ git checkout tags/xilinx-v2017.4

Next create a branch and check it out. It doesn’t matter what you call the branch. Here I call the branch fsbl_mods_2017.4

$ git branch fsbl_mods_2017.4
$ git checkout fsbl_mods_2017.4
Now edit the file and make the changes.
$ emacs lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c

Now we are ready to make the patch file. First add the file to the branch, and then commit the change.

$ git add lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
$ git commit -m "Patch to set MAC address based on dip switches" -s

Now we can create the patch.

$ git format-patch -1

This will create a lovely, albeit verbosely named, patch file


. How cool is that?

Here are the contents of my patch file.

From 1bc864fc9d064fd57c0721e27ca04e348d594bd9 Mon Sep 17 00:00:00 2001
From: Pete Johnson <pete@beyond-circuits.com>
Date: Mon, 21 May 2018 17:02:50 -0700
Subject: [PATCH] Patch to set MAC address based on dip switches

Signed-off-by: Pete Johnson <pete@beyond-circuits.com>
 lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c b/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
index 304a6db..d86522a 100644
--- a/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
+++ b/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
@@ -130,6 +130,9 @@ u32 FsblHookAfterBitstreamDload(void)
 u32 FsblHookBeforeHandoff(void)
 	u32 Status;
+	u32 dip_sw;
+	int i;
+	char ethaddr[40];
 	Status = XST_SUCCESS;
@@ -137,7 +140,13 @@ u32 FsblHookBeforeHandoff(void)
 	 * User logic to be added here.
 	 * Errors to be stored in the status variable and returned
+	Xil_Out32(0x41200004,0xf00); /* Set DIP switch as inputs */
+	dip_sw = Xil_In32(0x41200000)>>8; /* Read values */
 	fsbl_printf(DEBUG_INFO,"In FsblHookBeforeHandoff function \r\n");
+	sprintf(ethaddr,"ethaddr=%02x:%02x:%02x:%02x:%02x:%02x\n",0x00,0x0a,0x35,0x00,0x00,dip_sw);
+	for (i=0; i<strlen(ethaddr)+1; i++)
+	  Xil_Out8(0xfffffc00+i,ethaddr[i]);
 	return (Status);

Adding the patch file to Petalinux

Now we have our magic patch file. Next we need to add it to our Petalinux project. First we copy the patch file to the Petalinux project and rename it to something a little more sane.

$ cp 0001-Patch-to-set-MAC-address-based-on-dip-switches.patch petalinux-project/project-spec/meta-user/recipes-bsp/fsbl/files/0001-fsbl.patch

Next we create a file called petalinux-project/project-spec/meta-user/recipes-bsp/fsbl/fsbl_%.bbappend with the following contents

# Patch for FSBL
# Note: do_configure_prepend task section is required only for 2017.1 release
# Refer https://github.com/Xilinx/meta-xilinx-tools/blob/rel-v2017.2/classes/xsctbase.bbclass#L29-L35
do_configure() {
    if [ -d "${S}/patches" ]; then
       rm -rf ${S}/patches
    if [ -d "${S}/.pc" ]; then
       rm -rf ${S}/.pc
SRC_URI_append = " \
        file://0001-fsbl.patch \
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"


This bbappend script is run during the bitbake Petalinux build and it will patch the appropriate fsbl_hooks.c file during the build.

But wait there’s more…

Now of course we still need to modify the platform-top.h file for U-Boot. Hilariously I went down the same route for U-Boot only to find that there is no platform-top.h file in the git repository for U-Boot. However there is one in the project-spec directory created by petalinux-config. Hmmm… fool me once…

But left with no other choice I tried it – and what do you know? The build does pay attention to this file and U-Boot seems to be build with the addition that we made.

Are we done yet?

Not hardly. As of this writing when I do a Petalinux build the version that is built will not boot. But if I compile the FSBL in the tree that I checked out with my changes I can use that FSBL and boot successfully and the MAC address will be set according to the dip switches. If anyone knows what is going on here I would sure appreciate some advice.


When all is said and done I can build a Petalinux which does what I want. But I don’t understand why modifying platform-top.h in the Petalinux directory works but modifying the fsbl_hooks.c does not. And I’d like to know why the FSBL which gets built with my petalinux build does not work. When I do a find while bitbake is making the FSBL I see the appropriately patched fsbl_hooks.c down in the bowels of the bitbake build. And if there are any (and I mean any) differences in the patch file or the bbappend file then I get errors when bitbake runs. And where did the platform-top.h file come from anyway? That doesn’t seem to be iin the U-Boot tree at all.

Sometimes I think this may all be by design in order to keep embedded Linux consultants employed. Let me know if you have any insights.

GPIO with ZYNQ and PetaLinux

Accessing GPIO controllers is pretty straightforward with PetaLinux, but there are a few tricks you need to know.

Locating the GPIO controller

In the example FPGA I am using, there are two GPIO controllers in the programmable logic. These are at address 0x4120_0000 and 0x4121_0000. If you look in the pl.dtsi file in your PetaLinux project, in the directory subsystems/linux/configs/device-tree, you will see entries for the GPIO devices. There’s no need to modify the entire device tree.

If you make a PetaLinux build and boot it, you can look in the /sys/class/gpio directory.

root@pz-7015-2016-2:~# ls /sys/class/gpio/                       
export       gpiochip901  gpiochip902  gpiochip906  unexport

You can see that there is a gpiochip directory for each GPIO controller. The gpiochip901 and gpiochip902 directories correspond to the PL controllers that I added in my design. The gpiochip906 directory is for the GPIO controller in the PS.

How will you know which is which, though? Each directory contains a label file which tells you the device tree label for the controller. You can go ahead and look at the contents:

root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip901/label 
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip902/label 
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip906/label 

Looking at it, you’ll see that gpiochip901 corresponds to my controller at 0x4120_0000 and gpiochip902 corresponds to the controller at 0x4121_0000. Gpiochip906 is different, and corresponds to the built-in controller on the ZYNQ. Why those numbers? In my FPGA, the first GPIO controller controls only a single GPIO bit, while the second controls four bits. We can tell how many bits each controller controls by looking in the ngpio file for the controller.

root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip901/ngpio 
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip902/ngpio
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip906/ngpio

It looks to me like the numbering starts at 901. Since that controller has only a single GPIO bit, the next controller is 902. That one has four bits, so the ZYNQ PS controller goes at 906, which has 118 bits.

Enabling the GPIO bits

To access a GPIO bit, you need to enable the correct GPIO pin. You do that by writing to the export file in the /sys/class/gpio directory. Here is an example of enabling the LSB of my second controller:

root@pz-7015-2016-2:~# echo -n 902 > /sys/class/gpio/export 

Now if you look in the /sys/class/gpio directory, you will see a new directory created which allows you to control the individual GPIO pin.

root@pz-7015-2016-2:~# ls /sys/class/gpio
export       gpio902      gpiochip901  gpiochip902  gpiochip906  unexport

If you look in that directory you see a number of controls:

root@pz-7015-2016-2:~# ls /sys/class/gpio/gpio902
active_low  direction   power       subsystem   uevent      value

Accessing the GPIO bits

You can determine the GPIO direction by looking at the direction file. Since my GPIO pin is an output, it gives the value out.

root@pz-7015-2016-2:~# cat /sys/class/gpio/gpio902/direction

You can change the value to a 1 by writing to the value file.

root@pz-7015-2016-2:~# echo 1 > /sys/class/gpio/gpio902/value           


So there you have it. The “official” way to access GPIO on PetaLinux.

SPI with PetaLinux on ZYNQ

Recently, I spent a lot of time trying to get SPI working on a PicoZed ZYNQ board under Linux. It was absolutely shocking how complicated this ended up being. One issue, I think, is that the device tree options differ depending on which version of PetaLinux you’re using. In this post, I’m going to document here how to do it with PetaLinux 2016.2.

Modify the device tree

First, you need to modify the system-top.dts file located in your PetaLinux project’s subsystems/linux/configs/device-tree directory. You need to add an entry that extends the existing entry for the SPI device. In the example, I am using spi0 on the processor subsystem. You can see the base definition for the SPI interface in the zynq-7000.dtsi include file in the same directory.

It’s important to note that PetaLinux will create an entry for the SPI device when you configure Linux– however, you won’t get a device file unless you add the entry for your particular SPI device. The trick is to add the SPI device information to the file system-top.dts. The device tree specification syntax allows you to make changes to the automatic entry for the SPI device by labeling a a node, then overlaying additional information onto the labeled node in other parts of the device tree specification.

In our case, the processor built-in SPI devices are labeled spi0 and spi1. I wanted to use spi0, so I added an entry in the system-top.dts file to add to the spi0 definition. In the example below, I’ve added three devices.

&spi0 {
  is-decoded-cs = <0>;
  num-cs = <3>;
  status = "okay";
  spidev@0x00 {
    compatible = "spidev";
    spi-max-frequency = <1000000>;
    reg = <0>;
  spidev@0x01 {
    compatible = "spidev";
    spi-max-frequency = <1000000>;
    reg = <1>;
  spidev@0x02 {
    compatible = "spidev";
    spi-max-frequency = <1000000>;
    reg = <2>;

Rebuild linux and reboot your PicoZed board and you can now see the device files.

root@pz-7015-2016-2:~# ls -l /dev/spi*
crw-rw----    1 root     root      153,   0 Jan  1 00:00 /dev/spidev32766.0
crw-rw----    1 root     root      153,   1 Jan  1 00:00 /dev/spidev32766.1
crw-rw----    1 root     root      153,   2 Jan  1 00:00 /dev/spidev32766.2

Testing the SPI interface

In order to test the SPI interface, I built an FPGA with the SPI ports marked for debug. This allows me to use the embedded logic analyzer to view the pin activity from Vivado. PetaLinux ships with a program to test the SPI interface called spidev_test. I compiled it with the following command:

arm-xilinx-linux-gnueabi-gcc -o spidev_test /tools/xilinx/petalinux-v2016.2-final/components/linux-kernel/xlnx-4.4/Documentation/spi/spidev_test.c

Then, I copied it to my board using ssh, configured the logic analyzer to capture SPI activity, and ran the following command:

root@pz-7015-2016-2:~# ./spidev_test -D /dev/spidev32766.0 --speed 10000000
spi mode: 0x0
bits per word: 8
max speed: 10000000 Hz (10000 KHz)
RX | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | ................................

I could see the SPI pins wiggle in the logic analyzer view.


Anyway, I hope that this you save some time getting SPI to work.

PetaLinux for the PicoZed

I recently ran into a SNAFU when trying to build a PetaLinux image for a PicoZed board on a rev 2 FMC carrier board. The instructions assume that you have built an FPGA image and exported it from Vivado. You can check out my tutorial on PetaLinux for more background information, just in case you’re unfamiliar with the process.

You’re going to need to make sure that you have the board support package for the PicoZed board and carrier board you are using, which can download here. I like to keep the BSP in my Xilinx tool area, but you can store it anywhere you like. Be sure to unzip the download file.

Create the project

First, I source the settings file for PetaLinux. Don’t worry about the tftp warnings.

% . /tools/xilinx/petalinux-v2015.4-final/settings.sh

Now create your PetaLinux project. I created it as a sibling of the Vivado project directory.

% petalinux-create --type project --name pz_linux --source /tools/xilinx/pz_7015_2015_4.bsp

Configure the project

Next, you configure the project with the FPGA export. This sets the device tree for the build:

% petalinux-config --get-hw-description my_fpga/my_fpga.sdk --project pz_linux

Then, configure your filesystem:

% petalinux-config -c rootfs --project pz_linux

PetaLinux build fails

If we run petalinux-build right now, we run in to some trouble:

% petalinux-build --project pz_linux
INFO: Checking component...
INFO: Generating make files and build linux
INFO: Generating make files for the subcomponents of linux
INFO: Building linux
[INFO ] pre-build linux/rootfs/fwupgrade
[INFO ] pre-build linux/rootfs/gpio-demo
[INFO ] pre-build linux/rootfs/httpd_content
[INFO ] pre-build linux/rootfs/iperf3
[INFO ] pre-build linux/rootfs/peekpoke
[INFO ] pre-build linux/rootfs/weaved
[INFO ] build system.dtb
[ERROR] ERROR (phandle_references): Reference to non-existent node or label "usb_phy0"
[ERROR] ERROR: Input tree has errors, aborting (use -f to force output)
[ERROR] make[1]: *** [system.dtb] Error 255
ERROR: Failed to build linux

Fixing the device tree

The problem is that the device tree was overwritten by the petalinux-config. To fix that, we need to add the following lines to the system-conf.dtsi file located in pz_linux/subsystems/linux/configs/device-tree. Place the text following the entry for memory:

usb_phy0:phy0 {
  #phy-cells = ;
  reg = ;
  view-port = ;

With this change in place, we can go ahead and run petalinix-build again, and the build will complete. I’m not exactly sure why this procedure is required or how to patch the PetaLinux install to fix this. I will explore that later.


After building, you will need to package the distribution.

% petalinux-package --boot --format BIN --project pz_linux --fsbl pz_linux/images/linux/zynq_fsbl.elf --fpga my_fpga/impl_1/my_fpga.bit --u-boot

Copy the BOOT.BIN and pz_linux/images/linux/image.ub files to your MicroSD care and boot your PicoZed board.

Let me know if you have any ideas why this problem exists or how you can fix it.

Putting New Files in the Right Place: The Vivado Edition

Vivado has the ability to create and manage your own IP, which is a good thing. But there are some real gotchas when doing something like this, the first being that it doesn’t seem to place HDL files in the correct locations.

I have been running some FPGA workshops using the IP Packager in Vivado and having been having a lot of problems when creating new files in the IP component. Here’s an example:

From a Vivado project select Tools→Create and Package IP… Click Next, select Create a new AXI4 peripheral, click Next, just use the defaults for the next page, click Next again, use the defaults for the AXI information and click Next again, select Edit IP, then click Finish.

This will put in an IP Packager project. Edit the top level file myip_v1_0. I’m using Verilog but it works the same for VHDL. Instantiate a new dummy module at the bottom of the file:

// Add user logic here
dummy dummy (.clk(s00_axi_aclk));
// User logic ends

files_fig1Save the file and look at the design sources. You will see that there is now a new undefined module in the design called dummy. It should look like the figure to the right:

Now, we would like to create the new module. So we go to File→Add Sources… and create the new source file. Select Add or create design sources and click Next. Click Create File …, type the file name dummy and click OK. Click Finish. When the Define Module dialog pops up, just click OK. Click Yes to the dialog saying you haven’t changed anything. You now have a file defined for your dummy module.

files_fig2Ready to package that IP? Go to the Package IP tab in the Project Manager window. Then, select File Groups in the Packaging Steps window. You should see a window like the figure on the right.

Now click on the Merge changes from File Groups Wizard link. You will then get a link saying 2 warnings. Click on that, and you will see two warnings about your dummy.v file being on a path which is not relative to your IP root directory. What has happened is that Vivado placed the new file outside the IP component. If you go ahead and package your IP, you cannot use the packaged IP because it will be missing this file. Seems like a Vivado (2014.4) bug to me.

So how should we fix this? I don’t know how to move files in Vivado, but we can create the file in a different location when we make the new file. To do that, first delete the dummy file and remove it from disk. Just select the file, then right-click and select Remove File from Project. Select the checkbox to also delete it from disk and click OK.

files_fig3Now go to Add Sources again, and after clicking Create File…, specify the file name dummy.v, then select a new file location. files_fig4Navigate to your ip_repo area and into myip_1_0. Select the hdl directory, and click Select. Then click OK and then Finish to create the file. This will place the file in the proper location in the IP repository.

When you merge your changes in the File Groups step, you should not get any warnings. You can then package your IP and use it in your designs.

Hopefully this will be fixed in the next release of Vivado.

Remote JTAG with Vivado

My FPGA development computer is a server in a datacenter. It stores lots of memory and CPU, but it’s impossible to connect it directly to my development boards. ISE has always supported remote JTAG servers, but the implementation has always been spotty. In some releases it has difficulty with some versions of Linux. With Vivado, things are much improved.

If your lab machine is on the same network as your development machine, simply start the hw_server program in the Vivado bin directory and open a target from the development machine Vivado window. Just specify a remote server host name and use the default port 3121. It couldn’t be simpler.

If your lab machine is behind a firewall, you may need to use SSH to tunnel the traffic through. First, make sure the hardware manager is not running on your development machine. From the lab machine, ssh into the development machine with the following command:

ssh -R 3121:localhost:3121 development.my.com

This will create an SSH tunnel from your lab machine to your development machine. If you get a warning saying that SSH can’t bind to the required port it means you are still running a local JTAG server on your development machine. Assuming all went well you should be logged in to your development machine from the lab machine. You can then open the target in the hardware manager in Vivado but tell it to connect to a local machine. Vivido will see a server already running on port 3121 (through your SSH tunnel), so it won’t try to start a new one.

If your lab machine is running Windows then you can use the open source PuTTY program to make the tunnel. If your development machine is running Windows, then you need to install an SSH server on the machine. That’s beyond the scope of this post.

Verilog functions in Xilinx XST

A nice idea

The XST documentation says that Verilog functions are fully supported. I was frustrated today to discover that this is not the case. I was trying to make a library for handling fixed point arithmetic. Module instantiation overhead in Verilog is quite high in terms of lines of code and excess verbiage. All the code I need to use is combinational, so the natural thing to do is have one module with all my functions and parameters which control the representation of the fixed point numbers. After this, I can make instances of the library module with the various fixed point representations I need and use the functions.

So I whip out the following:

module SignedInteger
    parameter out_int = 1;
    parameter out_frac = 1;
    parameter in_a_int = out_int;
    parameter in_a_frac = out_frac;
    parameter in_b_int = in_a_int;
    parameter in_b_frac = in_a_frac;
    parameter out_width = out_int+out_frac;
    parameter in_a_width = in_a_int+in_a_frac;
    parameter in_b_width = in_b_int+in_b_frac;

    function signed [out_width-1:0] sum;
    input signed [in_a_width-1:0] a;
    input signed [in_b_width-1:0] b;
      if (in_a_frac > in_b_frac)
	sum = a + (b<<(in_a_frac-in_b_frac));
	sum = (a<<(in_b_frac-in_a_frac)) + b;


Now I can instantiate SignedInteger in another module and call the sum function. I need the function in another module because I may have multiple instances of SignedInteger in the calling module with different parameter values– sort of a poor man’s class mechanism. Everything simulates just peachy. Now, I synthesize a test case in XST.

Prepare to crash and burn

I’ll spare you the details, but XST has a number of issues with doing things like this, though the first few are not insurmountable. XST will first error out because I used concatenation instead of shift operators in the sum function. XST didn’t like the fact that one of the concatenation multipliers was negative. I got boxed into a corner here by Verilog too, because you can’t use a generate inside a function, and if I use a generate outside the function, it makes it really hard to call the function from outside the module. So, we’ll go back to the drawing board and use the shift operator. Next, XST believes that modules need at least one port. My guess is that the people who wrote XST never thought about just using a module as a container for its tasks and functions. Hmm… it’s not great, but I can add a port that I won’t use.

At this point, I’m faced with an interesting error message:

Analyzing top module .
ERROR:Xst:917 - Undeclared signal .
ERROR:Xst:2083 - "test.v" line 29: Unsupported range for function.

What do you mean, undeclared signal out_width? Firstly, it’s not a signal and secondly, it is so declared. The second message gives me pause. Crap– it really thinks that out_width is a signal. Why can’t it see it?

An experiment

Time for a test case. I try to synthesize this module, and guess what? It compiles without errors.

module test2
   input signed [15:0] a,
   input signed [15:0] b,
   output signed [19:0] sum

  localparam out_width = 20;
  localparam in_a_width = 16;
  localparam in_a_frac = 0;
  localparam in_b_width = 16;
  localparam in_b_frac = 4;

  SignedInteger #(16,4,16,0,12,4) si_16_4_16_0_12_4(.value());

  assign sum = si_16_4_16_0_12_4.sum(a,b);

If I give it the parameters it is looking for, the errors go away. I do get some strange warnings, however…

WARNING:Xst:616 - Invalid property "out_width 00000014": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "in_a_frac 00000000": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "in_a_int 00000010": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "in_a_width 00000010": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "in_b_frac 00000004": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "in_b_int 0000000C": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "in_b_width 00000010": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "out_frac 00000004": Did not attach to si_16_4_16_0_12_4.
WARNING:Xst:616 - Invalid property "out_int 00000010": Did not attach to si_16_4_16_0_12_4.

Not sure what that means exactly, but it sounds like something is rotten with the parameters.

Incorrect logic

Now this could cause incorrect logic to be synthesized. So I tried this test case.

module sub(empty);
  output empty;
  parameter paramvalue = 1;

  assign empty = 0;
  function [3:0] myparam_plus;
    input [3:0] in;
      myparam_plus = paramvalue + in;
endmodule // sub

module test3(value,empty);
  output [3:0] value;
  output empty;
  parameter paramvalue = 10;
  sub #(2) mysub(.empty(empty));
  assign value = mysub.myparam_plus(3);

The value output is a constant 5. That is, the sub module has a paramvalue of 2 and we add 3 to it. XST synthesizes this with no errors, warnings, or infos. The module is totally clean. Even so, the netlist it produces gives value a constant output of 13. Let’s see Xilinx support try to squirm their way out of this one. This seems like a bug to me.

At any rate, I have to give up on a pure Verilog solution to a fixed point library. Time to use Perl.