Nmigen UART Notes

I’ve been stumbling my way through nmigen. As I slowly figure things out, I’m dumping my notes here in case they prove useful…

The following is for the ICE40 HX8K evaluation board. It uses the uart.py example from the nmigen repository. The code will write bursts of a’s at 9600bps.

#!/usr/bin/python3

from nmigen import *
from uart import *
from nmigen_boards.ice40_hx8k_b_evn import *

class Top(Elaboratable):

    def elaborate(self, platform):

        m = Module()
        timer = Signal(26)

        #Connect TX to B12
        muart = platform.request('uart')

        m.d.sync += [
            timer.eq(timer + 1)
        ]

        uart = UART(divisor=1250)
        uart.tx_o = muart.tx

        m.d.comb += [
            uart.tx_data.eq(0x61),
            uart.tx_rdy.eq(timer[-1])
        ]

        m.submodules += [uart]

        return m

if __name__ == '__main__':
    platform = ICE40HX8KBEVNPlatform()

    platform.build(Top(), do_program=True)

This code sets tx_rdy high based on a counter. This means that tx_rdy stays high for multiple clock cycles. And as I understand it, the uart will continue to output for this period. The code above is available in this tarball.

The following code uses the uart to write data from some pins on the header of the ICE40 HX8K evaluation board. It adds 0x60 to the value from these pins so low values are in a readable range. A counter is used to set the UART ready signal high once every ~0.7s. There’s probably a better way of doing this… the code is also available in the tarball here.

#!/usr/bin/python3

from nmigen import *
from uart import *
from nmigen_boards.ice40_hx8k_b_evn import *
from nmigen.build import *

class Top(Elaboratable):

    def elaborate(self, platform):
        inpins = [ Resource("inpins", 0, Pins("B1 B2 C1 C2 D1 D2 E2 F1"), Attrs(IO_STANDARD="SB_LVCMOS")) ]
        platform.add_resources(inpins)

        m = Module()

        timer = Signal(24)

        #Connect TX to B12
        led0 = platform.request('led', 0)
        muart = platform.request('uart')
        data  = platform.request('inpins',0)
        dataA = Signal(8)
        rdy   = Signal(1)

        with m.If(timer[-1]):
            m.d.sync += [
              rdy.eq(1),
              timer.eq(0)
            ]
        with m.Else():
            m.d.sync += [
                timer.eq(timer + 1)
            ]

        with m.If(rdy == 1):
            m.d.sync += [
                rdy.eq(0)
            ]

        m.d.sync += [
            dataA.eq(data.i[0:8] + 0x60)
        ]

        m.d.comb += [
            led0.eq(timer[-1])
        ]

        # UART
        uart = UART(divisor=1250)
        uart.tx_o = muart.tx


        m.d.comb += [
            uart.tx_data.eq(dataA),
            uart.tx_rdy.eq(rdy)
        ]

        m.submodules += [uart]

        return m

if __name__ == '__main__':
    platform = ICE40HX8KBEVNPlatform()
    platform.build(Top(), do_program=True)

Nmigen PLL ICE40HX8K/HX1K

I’ve been playing with a couple of ICE40 boards. These are some quick notes on using the PLL with the HX8K and HX1K. There’s nothing particularly innovative here, but I wanted to get some notes down.

I used pll.py from this repository: https://github.com/kbob/nmigen-examples/tree/master/nmigen_lib

The examples in this repository are for the ICEBreaker platform, which uses ICE40UP5K. The code doesn’t work with the HX1K and HX8K as laid out on the ICEStick and ICE40HX8KBEVN (these are the boards supplied by Lattice).

In particular pll.py is hardcoded to use B_PLL40_PAD. It appears that the oscillator on the above platforms can not be connected to this type of PLL. As such, if you try and use pll.py unmodified with these platforms you’ll get the following error (as I understand it from nextpnr):

ERROR: PLL ‘U$$0.U$$0’ PACKAGEPIN SB_IO ‘clk12_0__io$sb_io’ is not connected to any PLL BEL

Instead I use SB_PLL40_CORE. This also means you need to use i_REFERENCECLK instead of i_PACKAGEPIN. So the following changes are required:

pll = Instance(“SB_PLL40_CORE”, becomes pll = Instance(“SB_PLL40_PAD”,

and

i_PACKAGEPIN=self.clk_pin, becomes i_REFERENCECLK=self.clk_pin,

In pll.py. A tarball containing a working example for the Icestick is below (if you want to use the HX8K, just change the platform).

Aside from the PLL stuff, I had an issue programming the ICE40HX8KBEVN. As I understand it “platform.build(Top(), do_program=True)” should program this board. iceprog gets called, and appears to succeed but the ICE40 doesn’t seem to get programmed correctly (possibly it needs to be erased first?). I need to run iceprog manually. Programming the Icestick doesn’t have this issue.

Tarball: here

Various OP177 die images

I bought some OP177G in SOIC8 on eBay. The strange thing about them was that while I bought 10 on the same piece of cut tape, the packages were different… I figured there was a high probability that they were fake.

I decapped them using an Olfa cutter (box cutter) blade. All things considered this decapping process works surprisingly well. With some effort, I think you can do better (@marcan42 has used this method with reasonable results which is what gave me the idea in the first place).

They were imaged under the same inspection microscope (Amscope) that I use for soldering. While the images are not great, the feature sizes are huge so it doesn’t matter so much.

I also purchased some OP177s from Digikey… turns out that while the packages look kind of fake, the dies appear to be identical!

ebay packages back
ebay packages front
Digikey package back
Digikey package front

More pictures follow:

Notes on the Keithley 2002 ADC

The Keithley 2002 is an 8.5 digit multimeter. This puts it among the highest precision multimeters available. I’ve been curious about the ADCs used in these high end multimeters and have been looking over the Keithley 2002 ADC schematic. These are my notes.

The schematic I used is from TiN. Originally this was hosted here but appears to currently be offline. A local copy of the schematic is here.

As far as I can understand the ADC is a dual slope implementation (UPDATE: it’s actually multislope, but the slow slope is always connected, see below). The slopes are driven by current sources of ~450uA (which are derived from a 7V reference). They are switched through a SD5400, and when not in use flow to some kind of current sink.

I’ve mostly looked at the current sources, and switching. As part of this, I’ve been playing with an LTSPICE simulation (download):

The Keithley 2002 uses a 7V LTZ1000 reference (generally regarded as the best voltage reference you can buy). This enters the ADC board as a differential signal (REFHI and REFLO). REFHI should be 7V and REFLO ~GND. The REFHI is used to feed a non-inverting opamp (U801). The opamp has gain of 1.666. A tap is taken off the feedback network of U801 at ~2.333V. This is used as the reference for U816. This opamp drives a JFET (Q805) such that 2.333V flows through it. Creating a current source. Setting the voltage through Q805 seems to set the current at ~-450uA.

Rather than being ground referenced, the feedback network of U801 is connected to the virtual ground of U802. U802 is referenced to REFLO. U802 is an inverting opamp. Again with a gain of 1.666. Again a tap is taken off the feedback network at ~-2.333V. This is used as the reference for a opamp driving a current source created by Q806 (similar to the above). Current though Q806 is ~450uA.

The above therefore creates positive and negative current sources of +/- 450uA. These are the run down slopes of this dual slope implementation.

There’s another current source, Q807. This goes directly to the integrator. I don’t understand it’s purpose…

UPDATE: Kleinstein on the EEVBlog forums pointed out that there is a single slower slope which is always connected, delivering ~5uA. I assume this is what U815/Q807 are. So this is in fact a multislope ADC, but the slow slope is always connected.

The references are switched through an SD5400. These are MOSFET switches. The Vgs threshold is ~0.1V. It is driven by TTL logic level signals (0,5V). This being the case, if the voltage coming from the current sources goes below -0.1V or above 5V it will not switch correctly. Because the current is so low (450uA), a load of >200Ohms would be required to generate a voltage higher than 0.1V. I assume that the integrator presents as a load lower than this.

When not in uses, both the input and the references switch to a separate path, I’ve not looked at this path in detail. But kind of imagine this is used as a sink, which keeps the current flowing through the references constant…