Resolved - Understanding the SB_IO primitive


The SB_IO primitive on ICE40 is a little confusing to me. Let’s focus on this particular use case in picosoc:

 SB_IO #( 
 	.PIN_TYPE(6'b 1010_01), 
 	.PULLUP(1'b 0) 
 ) flash_io_buf [3:0] ( 
 	.PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}), 
 	.OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}), 
 	.D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}), 
 	.D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}) 

I would appreciate a high-level description of the effect of the SB_IO cell under this particular configuration. Which pin is connected to which pin? Which are inputs and which are outputs? What is the purpose of instantiating this cell?

Fortunately, there is the documentation. Unfortunately, I cannot understand the circuit diagram on page 87.

I can recognize the D-flipflops, mux, OR-gates, and NOT-gates, etc. However, what is that triangle with a little circle to the right of PACKAGE_PIN. Also, what are the triangles that look like NOT-gates without the circle (e.g. the one to the upper left of PACKAGE_PIN)?


I am not an expert on this, but here is my understanding:

In order for a pin to be used for both input and output, it needs to use tri-state logic, so it can be HIGH or LOW when used for output, or high-impedance when used for input.

With most commercial FPGA tools, you can use 'bz to set a pin to high-impedance and the pin type and function can be deduced from the Verilog. Yosys, on the Ice40, however, has limited support for tri-state logic and usually only supports it in the top-level module.

You therefore need to use the SB_IO primitive explicitly for inout pins, particularly if the logic is not in the top-level module.

The pin type in the above example is set to 1010_01 so the input function is “Simple input pin (D_IN_0)” and the output function is “The output pin may be tristated using the enable”.

The equivalent verilog to that specific call, for each pin is:

assign flash_io0_di = flash_io0
assign flash_io0 = flash_io0_oe ? flash_io0_do :  1'bz

flash_io0 in the inout package pin
flash_io0_oe is the output enable signal. When that is high, you want the package pin set to output, else tri-stated
flash_io_di is the input value when the pin is tri-stated
flash_io_do is the value to set the pin to when output is enabled

You can use a call of SB_IO for each pin separately rather than do all 4 pins in one call, as in the example.


I see. That’s really helpful. Thanks!