Close

"That's Going to be SO EASY!"

A project log for CAT Board

The CAT Board is part of a Raspberry Pi-based hand-held FPGA programming system.

dave-vandenboutDave Vandenbout 11/30/2015 at 15:122 Comments

After struggling to get the tristate drivers working for the LED digit array, I decided I needed to test something nice and easy: the CAT Board DIP switches and push buttons. I would just display the hexadecimal digit for the four-bit DIP switch on the LED digits and then turn it on-and-off using the momentary pushbuttons. Simple!

The only part I was unsure about was enabling the built-in pullup resistors on the input pins of the iCE40 FPGA attached to the switches and buttons. (I use these so I don't need discrete pullups on the board, which saves PCB area and money for parts and assembly). But I looked in the source for some of the auxiliary MyHDL tools, and it appeared something like this could be used to turn on the pullups:

brd.add_port("gpio", pins=(23,24,25,26,), PULLUP='PULLUP')
So I wrote a bit of MyHDL code for the buttons+switches+LED test, compiled it, downloaded it to the CAT Board, and ... nothing. The LED digits displayed "0" no matter how I set the DIP switches, and the display never changed when I pressed the pushbuttons.

Probing the pins with an oscilloscope showed they were always at ground, so the pullups didn't look like they were enabled. I looked in the pin constraints file that was generated from my MyHDL code and the correct pin assignments were in there, but there was nothing about turning on the pullups. So I looked in the Verilog code that had been generated and it didn't have anything about the pullups either. Then I examined the code for the MyHDL converter and saw that it only processed the pin assignments and ignored everything else. So there's the problem.

To make some progress, I decided to add the pullup enables directly to the pin constraints file. I wasn't exactly sure of the syntax for doing that so I just added the string from the MyHDL code. The yosys Verilog synthesizer would probably throw an error when it saw that, but I could use that to track down the place in the code that handles pullups and get the correct syntax from there.

Surprisingly, yosys didn't complain when I ran it with the altered constraints file. But the new bitstream showed the same erroneous behavior when I downloaded it to the FPGA, so I still hadn't fixed the pullup problem.

If yosys was ignoring the pullup constraint, my next line of attack was to enter the constraint into the BLIF file that was generated and passed to the arachne-pnr place-and-route tool. As explained in this blog post, you can enable the pullup on an iCE40 FPGA input pin by changing the parameters for the associated SB_IO block. Unfortunately, when I looked into the BLIF file for my test circuit, there were no SB_IO blocks in it.

I was well-and-truly stuck, so I emailed Clifford Wolf and asked how I could use pullups with yosys. He replied that you currently had to instantiate them directly like this:

	module top(inout pin);
		wire outen, dout, din;

		SB_IO #(
			.PIN_TYPE(6'b 1010_01),
			.PULLUP(1'b 1)
		) io_pin (
			.PACKAGE_PIN(pin),
			.OUTPUT_ENABLE(outen),
			.D_OUT_0(dout),
			.D_IN_0(din)
		);
	endmodule
Now all I had to do was figure out how to do that in MyHDL, but it turns out it's already supported using user-defined code sections. All I had to do was define an input_pin module consisting of:
from myhdl import *

inst_num = 0

def input_pin(i, o, pullup=False):
    global inst_num
    inst_num += 1
    
    array = ''
    if len(i) > 1:
        array = '[{}:{}]'.format(len(i)-1, 0)
        
    pup = 0
    if pullup != False:
        pup = 1
    
    @always_comb
    def in_to_out():
        o.next = i

input_pin.verilog_code = \
"""
SB_IO
#(
    .PIN_TYPE(6'b000001),
    .PULLUP(1'b$pup)
) input_with_pullup_$inst_num $array (
    .PACKAGE_PIN($i),
    .D_IN_0($o)
);
"""
But after modifying my design to use the input_pin module, the MyHDL Verilog converter complained about unused input pins and undriven internal signals related to the switches and buttons. These errors occur because input_pin.verilog_code is just a text string so the converter can't interpret what the inputs and outputs of the SB_IO module are and whether they are used or not. It turns out I missed a crucial bit of the documentation and omitted the following lines from the Python routine:
def input_pin(i, o, pullup=False):
    global inst_num
    inst_num += 1
    
    array = ''
    if len(i) > 1:
        array = '[{}:{}]'.format(len(i)-1, 0)
        
    pup = 0
    if pullup != False:
        pup = 1
    
    @always_comb
    def in_to_out():
        o.next = i

    o.driven = True  # Tell converter this output is driven!
    i.driven = True  # Tell converter this input is used!

That was the final piece of the puzzle: now my design compiles and works correctly on the CAT Board. Here's a short video of the results from this long process:

Discussions

Yann Guidon / YGDES wrote 12/01/2015 at 00:36 point

Who said FPGA was simple ? ;-)

  Are you sure? yes | no

Jarrett wrote 12/01/2015 at 15:54 point

Nobody. Ever. ;)

  Are you sure? yes | no