craig@craigjb.com
Github
Twitter

Atlys reset shenanigans

I’ve been playing around the last few days building a Microblaze processor system on the Digilent Atlys Spartan 6 board. I think my end goal is to boot Linux on it–but for now I’ll settle for booting Das U-Boot. It’s definitely been an interesting journey, since I decided against using the Xilinx-specific git repositories for everything, and instead I used mainline checkouts wherever possible. In another post I’ll go through the hell it took to compile a working GCC with Glibc for little-endian Microblaze. But, in this post I wanted to point out something a little more devious.

In the Atlys board reference manual, you might see something like this: “One pushbutton has a red plunger and is labeled “reset” on the PCB silkscreen this button is no different than the other five, but it can be used as a reset input to processor systems.” And below it is a diagram like this:

Digilent Atlys manual section about push buttons

Well, both of these are lies. I spent so long trying to figure this out, a lot of it because the Xilinx system reset IP block will start running your code for a little while before the reset triggers and the system does nothing. On top of that, it’s not particularly deterministic, so I observed random behavior–not exactly conducive to debugging! After awhile, I knew the problem was with the reset circuit because the program would execute correctly if I held down the reset button. Also, it seemed odd that the processor did not reset itself after configuration, like it should have according to most sources I read. And the most damning evidence of all, the schematic! Here are the regular push-buttons:

Digilent Atlys push buttons schematic

And here is the reset button:

Digilent Atlys reset button schematic In case you are wondering, like I was, what the second reset button net is connected to, it goes to the Cypress USB chip for the Digilent programming interface.

That’s an active low reset! To fix the problem in the Xilinx XPS tool, flip the extern reset polarity on the proc_sys_reset block to “0”. And, you also have to flip the interrupt controller reset input polarity as well. Oh, and that will still give you a platform generator DRC error until you open the project .mhs file and change the external reset port RESET_POLARITY option to 0.

Now my system can send characters on the UART and correctly delay turning on some LEDs using simple delay loops. However, now I’m facing another fun problem: I can send data over the UART from the Microblaze, but so far I have been unable to receive data on the Microblaze. I’m using the Xilinx uartlite core, and the receive FIFO data valid bit in the status register never changes! I tried several different baud rates, checked all the .UCF constraints, and checked all the register addresses. Anyway, once I solve that problem, I can load a Das U-Boot image into the QSPI flash on the board, and hopefully start up the U-Boot console. Compiling U-Boot and configuring it is also a lot of fun, but I’ll write about that later.

Comments

Gameslab high-level design

I finally decided I might as well make an overall block diagram of the Gameslab system. I was working on changing the design schematics and realized I forgot a couple things. So, I made this block diagram to use as a checklist–makes sure I don’t forget pieces. It also helped to figure out which peripherals would connect to the Zynq PS versus the PL. For example, the LCD touch controller was originally connected to PS I2C pins, but the PL has to be enabled to use the LCD anyway. So, it made more sense to route the touch screen I2C bus through the EMIO.

Gameslab block diagram In the Zynq SOC, PS refers to the ARM Cortex A9 sub-system, and PL refers to the programmable logic section. EMIO is a function for passing PS I/O through the PL fabric. Grey blocks will be implemented in programmable logic.

After thinking about what a pain it would be to monitor voltages and currents with the Zynq XADC and do power management with the huge ARM PS with U Boot and Linux, I added a system management controller to the design (SMC). I realized that something had to monitor and control the power-up sequence, and only boot the Zynq when the battery status allows. Doing this with the XADC in the Zynq would require booting the entire processor and PL section, and possibly loading a bitstream–all that just to check the battery voltage. In addition, I wanted to use a momentary slide switch for controlling power, mostly because they feel nice. However, if I hooked this to the Zynq, it’d have to be on some GPIO interrupt, which means the Zynq would boot by default when power is present, power down, and then wait for the switch interrupt. I’d rather keep the whole Zynq powered down when in standby. I don’t plan on my embedded Linux distro taking long to boot.

So, in this revised design, the SMC is a PIC18 microcontroller with its ADC connected to the battery voltage, system voltages, and a system current sense amplifier. It also controls the battery PMIC through an I2C interface. This way, when power is applied, the SMC starts up and monitors the power supplies. If the slide switch is pulled and sufficient battery charge is available, the SMC triggers the Zynq power-up reset. I chose the PIC18 family because I’ve used them on dozens of previous projects, so they’re familiar. However, any 8-bit micro would do.

For the PMIC, I want a chip that will charge with at least 1.5 A, since I want to use three 2500 mAh lithium polymer batteries. I found a couple TI chips that could work, and I’ll post more details when I go through the power system. I also took a look at loads of switching voltage regulators, and I think I’ve decided on all discrete switchers. There are some large chips that integrate 3-6 switching regulators in one, but they all either have the wrong combination of output voltage ranges, currents, or number of regulators. Plus, I want to individually control each supply. This way, the SMC can properly sequence power for the Zynq, and keep the PL shut-down when not necessary. The LEDs on the SMC will indicate charging and power status. Red will mean it’s charging, green will mean the system is on.

I debated whether I should add Wi-fi to the system, using a TI industrial Wi-fi module on an SDIO interface. For example, the TI WL1807MOD is a high-speed completely integrated Wi-fi module. Add antennas, power, and a passive or two, and it does 80 Mbps 802.11n. However, I think for this first revision, network-over-USB will be fine. USB networking will be much simpler to get working in U Boot and Linux.

I will use the XADC for the thumbsticks though! Originally I had them connected to the SMC as well, since I was already using the ADC there. However, The Zynq has the XADC, and it might be interesting to use custom PL hardware to filter the thumbstick input. The buttons also go through the PL pins. These can either pass through the EMIO to the PS GPIO controller, or the custom PL logic can manage them.

Well, I think I pretty much have a plan now. So time to finish fiddling around with my schematics so I can finish this PCB design.

Comments

Basic IBIS simulation with eispice

While working on the DDR3 interface of the re-imagined Gameslab, I wanted to do some simple sanity checks for signal integrity. Since I don’t really want to pay >$1000 for two PCBs with controlled impedance and a custom stackup, I just wanted to verify my cheap boards from PCB-POOL would work at all. PCB POOL lists the stackup for 6-layer boards on their site, so I plugged these specs into a trace impedance calculator. My best option so far looks like routing the high-speed DDR traces on the inner layers with a reference ground plane above each signal layer. I calculated the impedance at ~63 Ω. Since DDR3 has 34 Ω driver and typically 40 Ω transmission lines, I wanted to at least do a sanity check simulation.

Sadly, there are not many free or open-source options for doing SI simulations. The one I found, eispice supports IBIS models, but it took a bit of work to get the simulation working. The IBIS support isn’t comprehensive, and it seems eispice only supports simulating a rising waveform or a falling waveform, no repetition. Anyway, here’s the simple circuit I wanted to simulate:

Simple model for simulating signal integrity

This represents one of the address/command/control pins on the Zynq series terminated and connected to the DDR3 SDRAM through a 63 Ω impedance trace. I want to see if different series termination resistors have any effect, and I want to see if the rise time is near adequate. The eispice examples page has a circuit that is very close:

# example from http://www.thedigitalmachine.net/eispice.examples.html
import eispice
ibs = eispice.Ibis('test')
cct = eispice.Circuit('IBIS Test')
cct.Driver = ibs['2']('vs')
cct.Rt = eispice.R('vs', 'vi', 33.2)
cct.Tg = eispice.T('vi', 0, 'vo', 0, 50, '2n')
cct.Receiver = ibs['1']('vo')
cct.tran('0.01n', '20n')
eispice.plot(cct)

Now, grab the Xilinx Zynq IBIS models and the Micron DDR3 IBIS model. If you try to import the models as they are, you will get a result like below:

In [1]: import eispice

In [2]: zynq_ibis = eispice.Ibis('zynq7.ibs')
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-2-41cc8ef3ed85> in <module>()
----> 1 zynq_ibis = eispice.Ibis('zynq7.ibs')

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis.pyc in __init__(self, filename, device)
     92 		"""
     93
---> 94                 Ibis_Parser.__init__(self, filename, device)
     95
     96         def __getitem__(self, pin):

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in __init__(self, filename, device)
    560                 self.addRE(_reAny, self.handleUnkown)
    561
--> 562                 self.process()
    563
    564                 self.fdin.close()

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in process(self)
     99         def process(self):
    100                 for line in iter(self.fdin.readline, ''):
--> 101                         if self._process(line) is Done:
    102                                 self.fdin.seek(-len(line),1)
    103                                 break

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in _process(self, line)
     92                         if match:
     93                                 if callable(handler):
---> 94                                         return handler(**match.groupdict())
     95                                 else:
     96                                         return handler

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in handleUnkown(self, line)
    167
    168         def handleUnkown(self, line):
--> 169                 raise RuntimeError, 'Unsupported Line\n%s' % line
    170
    171 # -------------------------------------------------------------------------- #

RuntimeError: Unsupported Line
[r series]             77.8408           97.4321             61.2294

Well, that looks pretty disappointing. However, if you open up the IBIS file and take a look, there’s a ton of irrelevant stuff for this simulation. And, the above tries to import all of it. Maybe if all the rest is stripped out, the problem might look more solvable. So, I opened up the IBIS model in a text editor, and started ripping out sections. First, eispice choked on Windows-style carriage returns, so strip those. I put some notes on the rest inline here:

...
 
| looks like good stuff to keep
[Component]      ZYNQ7
[Manufacturer]   Xilinx Inc.
[Package]
 
...
 
| WATCH OUT HERE, you need to pick the right section and uncomment it!
| I'm doing a DDR sim, so I uncommented the section for my package under
| the ps_ddr header
|
| Zynq ps_ddr Package Parasitics 
|
| Date: 7/30/12 Initial Release
| R_pkg values characterized at DC
|
| If utilizing the package typ, min, max data in your simulations
| uncomment the desired R_pkg, L_pkg and C_pkg lines.
| Be sure to comment out all the unused lines in this section.
| 
| Note that the detailed and fully coupled .pkg file is available for
| all these packages, and may be requested and invoked in the 
| [package model] section.
|
 
...
 
|CLG225
|variable        typ            min             max
R_pkg            347.3m    161.7m        566.2m
L_pkg        6.11nH    2.18nH        10.31nH        
C_pkg           0.97pF    0.51pF        1.59pF
 
| Things get interesting here. This IBIS file supports multiple packages,
| so the pin names don't matter too much. Xilinx docs say to use the 
| SSTL15_S_PSDDR, so I just need that on a pin name I can use.
|************************************************************************
[Pin]  signal_name          model_name 
1      SSTL15_S_PSDDR       SSTL15_S_PSDDR
 
...
 
| Now there are a ton of model sections. Look for the SSTL15_S_PSDDR model
| or whatever one you want to simulate, and strip out everything else
 
...
 
|************************************************************************
|                          Model SSTL15_S_PSDDR
|************************************************************************
|  
[Model]  SSTL15_S_PSDDR
Model_type I/O
Polarity       Non-Inverting
Enable        Active-Low
Vinl = 0.65
Vinh = 0.85
Vmeas =  0.7500V
Cref =   0.0F
Rref =   50.0000
Vref =   0.7500V
C_comp   2.70pF 2.69pF  2.70pF
|  
[Temperature Range]   25.0000           85.0000             0.0
[Voltage Range]        1.5000V           1.4250V             1.5750V
[Pulldown] 
|Voltage   I(typ)              I(min)              I(max)
|  
-1.50    -37.5000mA    -28.9000mA    -43.2200mA 
-1.45    -37.5000mA    -28.9000mA    -43.2200mA 
...
2.95    30.0000mA    21.1000mA    37.0000mA 
3.00    30.0000mA    21.1000mA    37.0000mA 
|  
[Pullup] 
|Voltage   I(typ)              I(min)              I(max)
|  
-1.50    60.4000mA    46.2000mA    69.7200mA
-1.45    60.4000mA    46.2000mA    69.7200mA
...
2.95    -30.7000mA    -22.0000mA    -41.2000mA
3.00    -30.7000mA    -22.0000mA    -41.2000mA
|  
[GND_clamp] 
|Voltage   I(typ)              I(min)              I(max)
|  
  -1.50    -6.2610A          -5.9750A            -6.4580A
  -1.46    -5.7940A          -5.5750A            -5.9530A
...
  1.46    1.9540nA          35.2100nA           1.7260nA
  1.50    2.6280nA          79.8600nA           1.9850nA
|  
[POWER_clamp] 
|Voltage   I(typ)              I(min)              I(max)
|  
  -1.50   0.8252A           0.9116A             0.7915A 
  -1.48   0.7942A           0.8811A             0.7604A 
...
  -0.02   3.4410nA          28.0400nA           4.6030nA 
  0.00   2.6280nA          22.4700nA           3.3260nA 
|  
[Ramp]  
| variable       typ                 min                 max 
dV/dt_r  0.5864/0.1837n      0.488/0.2561n      0.6801/0.1475n
dV/dt_f  0.6414/0.1844n      0.4812/0.2714n      0.7194/0.1347n
R_load = 50.0000
|  
[Rising Waveform] 
R_fixture= 50.0000
V_fixture= 0.7500
V_fixture_min= 0.7125
V_fixture_max= 0.7875
|time     V(typ)              V(min)              V(max)
|  
0.00S          201.0000mV     248.6000mV     174.8000mV     
30.00pS        201.0000mV     248.6000mV     174.7000mV     
...     
1.64nS         1.2700V        1.1510V        1.3730V        
1.67nS         1.2700V        1.1510V        1.3730V        
|  
[Rising Waveform] 
R_fixture= 50.0000
V_fixture= 0.0
|time     V(typ)              V(min)              V(max)
|  
0.00S          -1.3070uV      -259.5000nV    -1.8172uV      
30.00pS        -1.4919uV      -1.0851uV      -12.9827uV     
...       
2.21nS         975.3000mV     781.3000mV     1.1260V        
2.24nS         975.3000mV     781.3000mV     1.1260V        
|  
[Falling Waveform] 
R_fixture= 50.0000
V_fixture= 0.7500
V_fixture_min= 0.7125
V_fixture_max= 0.7875
|time     V(typ)              V(min)              V(max)
|  
0.00S          1.2700V        1.1510V        1.3730V        
30.00pS        1.2700V        1.1510V        1.3730V            
...   
1.85nS         201.0000mV     248.7000mV     174.8000mV     
1.88nS         201.0000mV     248.7000mV     174.8000mV     
|  
[Falling Waveform] 
R_fixture= 50.0000
V_fixture= 0.0
|time     V(typ)              V(min)              V(max)
|  
0.00S          975.3000mV     781.3000mV     1.1260V        
30.00pS        975.3000mV     781.3000mV     1.1260V        
...    
1.46nS         42.4740uV      66.2950uV      35.2830uV      
1.49nS         40.9790uV      60.0000uV      33.8900uV      
|  
|End [Model] SSTL15_S_PSDDR 
|End [Component] 
|
 
...
 
|  
|*************************************************************************
[END]
|

And now if we try it, this time instantiating a driver device from a pin model:

In [1]: import eispice
 
In [2]: zynq_ibis = eispice.Ibis('zynq7.ibs')
 
In [3]: cct = eispice.Circuit('DDR3 sim')
 
# here '1' is the pin name from above
# and 'vs' is the driver output node name (like a SPICE node)
In [4]: cct.Driver = zynq_ibis['1']('vs')

No error! Now, if I try the Micron IBIS model, well it breaks too, but with a different error:

In [1]: import eispice
 
In [2]: micron_ibis = eispice.Ibis('v89c.ibs')
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-2-32991fd5d268> in <module>()
----> 1 micron_ibis = eispice.Ibis('v89c.ibs')
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis.pyc in __init__(self, filename, device)
     92 		"""
     93 
---> 94                 Ibis_Parser.__init__(self, filename, device)
     95 
     96         def __getitem__(self, pin):
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in __init__(self, filename, device)
    560                 self.addRE(_reAny, self.handleUnkown)
    561 
--> 562                 self.process()
    563 
    564                 self.fdin.close()
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in process(self)
     99         def process(self):
    100                 for line in iter(self.fdin.readline, ''):
--> 101                         if self._process(line) is Done:
    102                                 self.fdin.seek(-len(line),1)
    103                                 break
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in _process(self, line)
     92                         if match:
     93                                 if callable(handler):
---> 94                                         return handler(**match.groupdict())
     95                                 else:
     96                                         return handler
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_parser.pyc in handleUnkown(self, line)
    167 
    168         def handleUnkown(self, line):
--> 169                 raise RuntimeError, 'Unsupported Line\n%s' % line
    170 
    171 # -------------------------------------------------------------------------- #
 
RuntimeError: Unsupported Line
[package model] v89c_78ball_pkg

Well, might as well strip out all the unnecessary stuff here too:

...
 
|
[IBIS Ver]    4.2
[File Name]   v89c.ibs
[Date]        02/03/2012
[File Rev]    2.0 
[Source]      From silicon level SPICE model at Micron Technology, Inc.
              For support e-mail modelsupport@micron.com
|
[Copyright]   Copyright 2012 Micron Technology, Inc. All rights reserved.
 
...
 
| WATCH OUT, there are multiple component definitions in here
| strip out the other ones and leave only the component of interest
|***************************************************************************
| COMPONENT: MT41J128M16JT (96-Ball FBGA, x16, 8mm X 14mm)
|***************************************************************************
|
[Component]     MT41J128M16JT
| WATCH OUT, eispice chokes on this line below, so comment out
|[Package Model] v89c_96ball_pkg
[Manufacturer]  Micron Technology,Inc.
[Package]
|              typ         min          max
R_pkg          345.04m     192.55m      567.10m
L_pkg          1.78nH      0.861nH      3.365nH
C_pkg          0.36pF      0.254pF      0.524pF
|
 
...
 
| I decided to simulate the A0 pin, arbitrarily
| WATCH OUT, eispice turns all pin names to lower case for some reason
| and it IS case sensitive!
| AND, change "INPUT2" to the full model name "INPUT2_1600"
| so we don't have to use model selector stuff
[Pin]          signal_name model_name   R_pin        L_pin        C_pin
N3             A0          INPUT2_1600       324.54m      1.598nH      0.353pF
 
...
 
| Grab just the relevant model (by speed grade too, e.g. the "_1600" part)
| it looks a little different in this IBIS file, but we'll see
|
|***************************************************************************
| MODEL INPUT2_1600 (Add/Cmd/Ctrl Input Model, 1333/1600Mbps)
|***************************************************************************
|
[Model]        INPUT2_1600
Model_type     Input
|
Vinl = 600.000mV
Vinh = 900.000mV
|
|                            typ                 min                 max
|
C_comp                      0.660pF             0.585pF             0.735pF
|
[Model Spec]
| Input threshold voltage corners
Vinl                        0.6000V             0.5625V             0.6375V
Vinh                        0.9000V             0.8625V             0.9375V
|
| Dynamic Overshoot Parameters from DDR3 Specification
|D_overshoot_ampl_h          0.40                NA                  NA
|D_overshoot_ampl_l          0.40                NA                  NA
|D_overshoot_area_h          0.33n               NA                  NA
|D_overshoot_area_l          0.33n               NA                  NA
|
| WATCH OUT, eispice chokes on this section below, comment out
|[Receiver Thresholds]
|Vth      =  0.750V
|Vth_min  =  0.7350V
|Vth_max  =  0.7650V
|Vinh_ac  =  0.150V
|Vinh_dc  =  0.100V
|Vinl_ac  = -0.150V
|Vinl_dc  = -0.100V
|Tslew_ac =  5.000ns |Not specified, so set to high value
|Threshold_sensitivity = 0.50
|Reference_supply Power_clamp_ref
|
[Voltage Range]             1.5000V             1.4250V             1.5750V
[POWER Clamp Reference]     1.5000V             1.4250V             1.5750V
| Junction Temperature (Ambient temp is 35C typ, 95C min, 0C max)
[Temperature Range]        50.0               110.0                 0.0
|
|***************************************************************************
|
[GND Clamp]
|
|       Voltage            I(typ)             I(min)             I(max)
|
     -1.50000000E+0   -131.93755000E-3   -106.31704000E-3   -152.40004000E-3
     -1.49500000E+0   -130.41340000E-3   -104.87407000E-3   -150.81963000E-3
...
   -290.00000000E-3                 NA                 NA      0.00000000E+0
      1.57500000E+0      0.00000000E+0      0.00000000E+0      0.00000000E+0
      3.00000000E+0      0.00000000E+0      0.00000000E+0      0.00000000E+0
|
[POWER Clamp]
|
|       Voltage            I(typ)             I(min)             I(max)
|
     -1.50000000E+0     94.89989300E-3     90.42406100E-3     97.73690400E-3
     -1.49500000E+0     94.15744300E-3     89.69524700E-3     96.98225500E-3
...
   -290.00000000E-3      0.00000000E+0                 NA                 NA
      1.57500000E+0      0.00000000E+0      0.00000000E+0      0.00000000E+0
      3.00000000E+0      0.00000000E+0      0.00000000E+0      0.00000000E+0
|
| There's some extra stuff here like sparse package models etc. but I had 
| trouble getting it to work
 
[End]

Cool, now it seems to import fine. Let’s run the simulation:

In [1]: import eispice

In [2]: zynq_ibis = eispice.Ibis('zynq7.ibs')

In [3]: micron_ibis = eispice.Ibis('v89c.ibs')

In [4]: cct = eispice.Circuit('IBIS DDR3 Sim')

In [5]: cct.Driver = zynq_ibis['1']('vs')

# note the lower-case 'n3'
In [6]: cct.Receiver = micron_ibis['n3']('vo')

Well that blew up too:

In [6]: cct.Receiver = micron_ibis['n3']('vo')
ERROR:simulator ./module/simulatormodule.c:79	r->pw == NULL -- 
ERROR:simulator ./module/simulatormodule.c:99	pwInit(r, args, kwds) -- 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-13833dc28005> in <module>()
----> 1 cct.Receiver = micron_ibis['n3']('vo')
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis.pyc in __call__(self, node, speed, direction, io, modelName)
    103 			def __call__(self, node, speed=Typical, direction=Rising, io=Output,
    104 					modelName=None):
--> 105                                 return Pin(self.ibs, self.pin, node, speed, direction, io, modelName)
    106 
    107                 return PinBuilder(pin.lower(), self)
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_device.pyc in __init__(self, ibs, pinName, node, speed, direction, io, modelName)
    221 				((model.model_type == 'i/o') and io==Input)):
    222 
--> 223                         self.Buffer = Receiver(die, vcc, 0, model, speed)
    224 
    225 		elif ((model.model_type == 'output') or 
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/ibis_device.pyc in __init__(self, iNode, pwrNode, gndNode, model, speed)
     56                 if hasattr(model, 'gnd_clamp'):
     57 			self.VI_gnd = device.VI(iNode, gndNode, 
---> 58 					waveform.PWL(model.gnd_clamp[speed]))
     59                 if hasattr(model, 'power_clamp'):
     60 			self.VI_pwr = device.VI(pwrNode, iNode, 
 
/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/eispice/waveform.pyc in __init__(self, data)
     68                 data = units.floatList2D(data)
     69                 data = data[data[:,0].argsort(),] # sort by first column for simulator
---> 70                 simulator_.PWL_.__init__(self, data)
     71 
     72 class PWC(simulator_.PWC_):
 
TypeError: Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe'

After banging my head for awhile, I found out this is related to certain fields in the IBIS having “NA” values. I went back and changed them to 0.0000, fitting in with the values on either side.

| example of an NA value
| I just changed them to the 0 value
|   -290.00000000E-3      0.00000000E+0         NA        NA
| so:
   -290.00000000E-3      0.00000000E+0      0.00000000E+0      0.00000000E+0

Now, running the simulation:

In [1]: import eispice
 
In [2]: zynq_ibis = eispice.Ibis('zynq7.ibs')
 
In [3]: micron_ibis = eispice.Ibis('v89c.ibs')
 
In [4]: cct = eispice.Circuit('IBIS DDR3 Sim')
 
In [5]: cct.Driver = zynq_ibis['1']('vs')
 
In [6]: cct.Receiver = micron_ibis['n3']('vo')
 
# 40 ohm series term resistor
In [7]: cct.Rt = eispice.R('vs', 'vi', 40)
 
# eispice.GND is the return on each side of the transmission line
# 63 is the characteristic impedance, and 260p is the propagation
# for 1.5 inches of this line delay
In [8]: cct.Tline = eispice.T('vi', eispice.GND, 'vo', eispice.GND, 63, '260p')
 
In [9]: cct.tran('0.01n', '10n')
 
In [10]: eispice.plot(cct)

And finally, a result!

Plot of IBIS DDR3 signal integrity simulation

I wanted to do a few more analyses that eispice doesn’t support very well. Luckily, it’s pretty simple to dump the result data.

# looked in eispice modules/plot.py to figure this out
In [11]: vo_data = cct.results[cct.variables.index('v(vo)')]
 
In [12]: time = cct.results[0]
 
# do anything you want with the results here
# matplotlib is nice :)
Comments

Gameslab first steps

I bought one of the PSP LCD screens from Sparkfun forever ago, planning on building some kind of game console-like device with it. Slowly, I accumulated more hopeful parts, like the PSP-style thumb-sticks and a touchscreen that fits the LCD perfectly. All these parts are screaming at me to build something cool, so I’m going to build an FPGA based, hand-held game console. From scratch.

Building a hand-held console is going to be very different than my previous game console builds, since the small form-factor requires designing the mechanical and electrical components together. I’m not really a fan of the 3D-printed look for project cases, so I want to use some kind of milled aluminum for the front and back panels. Ideally I can have some engravings and maybe even color fills. I might still use 3D-printing for building some side-walls or grips pieces to make the metal edges more comfortable.

Since I have previous experience with Xilinx FPGAs (specifically the Spartan 6 series), I decided on a Spartan 6 part: the XC6SLX25. This is the largest logic cell count available in a decently solderable package. The FTBGA package is 1mm pitch, which is doable with OSH Park PCBs and a toaster oven. Other than the FPGA, the console requires battery charging, power regulation, memory (probably DDR), LCD interface, touchscreen interface, audio (headphones and maybe speakers), thumb-stick interfaces, SD card, some buttons, and maybe Wi-Fi.

Some 3D CAD modeling

I decided to model most of the mechanical components using SolidWorks (it’s what I know how to use). I’ll need to position all of the components before I can design the PCD. The LCD connectors, thumb-stick connections, buttons, battery connectors, power connectors, and SD card slot all depend on the overall mechanical layout. And, there is not going to be a lot of spare room.

Model of the Gameslab LCD part Model of the Gameslab thumbstick part Left: the LCD CAD model; right: the thumb-stick CAD model.

Planning

Several parts of the electrical design will be tricky; I expect designing the FPGA power regulation and DDR interface will be the most painful. To alleviate some of the pain, I’m using the Digilent Atlys board schematics as a reference. However, I have to be careful, since the Atlys was designed to run off of a wall wart. The Gameslab needs to have decent battery life running on rechargeable batteries. For example, the DDR2 address lines on the Atlys are parallel terminated with ~50 Ω resistors.There are 13 address lines, and each line swings between 0 V and 1.8 V. Therefore, each address line will have a DC power dissipation of approximately 16.2 mW. Together, that’s 210 mW! I plan on using one or two lithium polymer batteries, maybe 2000 mA each at 3.7 V; for one cell, that’s 7.4 Watt-hours. I want 4-5 hours of battery life, so system dissipation should be around 1.5 W, and 210 mW is 13.4% of that–just for termination!

The Atlys board uses some really nice Linear Technology switching regulators for the 3.3 V rail, 1.8 V rail, and the 0.9 V DDR2 termination reference. I don’t feel like paying $7 per regulator, so I’m going to do some shopping. I already found a replacement for the LTC3413 (the DDR2 0.9 V reference regulator). On Digikey, the LTC3413 costs $7.94 per unit (in onesey-twosey quantities). The NCP51199 DDR2 termination regulator from ON Semi costs just $0.70.

I’ve also given the FPGA configuration at startup some thought. FPGAs are designed to load their configuration from an external memory (SPI or parallel flash typically) at startup. However, wouldn’t be interesting if every game could have completely custom hardware? That’s why I’m looking at including a microcontroller to interface with the SD card and perform the FPGA configuration and re-configuration. The microcontroller can load a default FPGA configuration that will present a game selection screen, and then once a game is selected, the FPGA will communicate with microcontroller, instructing it to re-configure the FPGA with the selected game image. The XC6SLX25 has 6,440,432 configuration bits (The total number of configuration bits was hard to find. It’s buried in this document at the end of page 75), and with a maximum configuration serial slave clock of 80 MHz, that would take 80 ms to reconfigure–about 5 frames at 60 FPS.

Since power is going to be a big design issue, I thought I’d do a quick estimate of the likely power consumption. I have a previous FPGA design for the Atlys board that contains a Microblaze soft processor, DDR2 interface, and an HDMI output. Since the Atlys board uses an FPGA almost twice the size and the design is much higher speed, the estimate should be conservative. I used the Xilinx XPower Analyzer, and came up with the values below:

Supply Rail Voltage Current Power
VCCINT 1.2 V 0.370 A 0.444 W
VCCAUX 3.3 V 0.108 A 0.356 W
VCCO33 3.3 V 0.022 A 0.073 W
VCC018 1.8 V 0.196 A 0.353 W
    Total: 1.226 W

Obviously this is going to be pushing the limit of a single 2000 mAh lithium polymer cell. The Gameslab will likely require two of the cells, since 1.226 W eats almost 82% of the power budget already.

Comments

Looking back at Gamesphere

Before I write much about my Gameslab project, I think it’s worth looking back at my Gamesphere project from over four years ago. I still receive a handful of emails per month asking for schematics or help on building a new game console. Usually, these emails are desperate college seniors looking for an easy senior project. And, I’m afraid for the people that are legitimately looking for schematics to learn from, I didn’t make any. That’s part of why I want to look back on my previous project; I want to see what I can do better this time around, and also, what I did well the first time.

Gamesphere overview Top, from left to right: CPU board, video board; bottom: N64 controller, input board

I started building the first Gamesphere during my senior year of high school. I had already begun experimenting with PIC microcontrollers and other breadboard-able goodies before, but Gamesphere was my biggest project yet at the time.

I had already built several microcontroller-based game (-ish) projects using PIC microcontroller and some careful clock cycle counting. I don’t have any surviving photos, but I think the first one was a black-and-white pong game that generated NTSC video using a PIC18F452. Another project used a 128×128, 8-BPP cellphone screen, a few tactile switches, and a PIC18F4685. The cellphone screen used an SPI interface, so I had use dirty-rectangle techniques to achieve decent draw-rates. Having built several consoles, I really wanted to do something substantial. I found some incredible inspiration in André LaMothe’s book The Black Art of Video Game Console Design. Andre also used to maintain a website dedicated to the game consoles in the book, and a picture gallery of all the WIP consoles leading up to the XGameStation. I was inspired by the video in which Andre shows off several hundred lines of ARM assembly, assembles the project, burns it to an EPROM, and the boots the wire-wrapped ARM board–all to blink a LED. I wanted to do that.

I actually copied André’s choice of microcontroller, an Atmel AT91R40008. The AT91R40008 is a 32-bit ARM7/Thumb micro with a 16-bit external parallel bus. It was specified to run up to 75Mhz, but I’ve used my parts all the way to 100MHz. Atmel, at least back then, had a very generous free sample program. I signed up, said I was a student, and parts showed up two weeks later! In addition to samples, Atmel datasheets are incredibly good (AT91R40008 datasheet). Trust me, try using a Freescale, or even worse, a Cirrus datasheet.

Gamesphere screenshot of tile based rendering and scrolling The vertical line artifacts are from SRAM bank cross-overs–one of the downsides of a direct DAC. Thank you Ari Feldman for SpriteLib

CPU Board

Gamesphere CPU board front side The big mass of resistors is my attempt at an R2R ladder audio DAC. It actually worked, but the tone was completely off. I never did figure out whether it was my sample-rate timer or the DAC itself.
Gamesphere CPU board back side That is point-to-point soldering hell. Only when you’re in high school with gobs of free-time…

Now before you ask, no I don’t have schematics. That was actually one of my biggest mistakes: I simply added components and connections as I needed them. If you look at the point-to-point soldering hell on the bottom of the board, I feel like the lack of schematic is quite evident.

I started the CPU board with the power supply: just a standard barrel jack, a 7805 for 5V, a LD33V for 3.3V, and a LM317 set for 1.8V. Each power supply has its own status LED (hidden under the R2R DAC in the picture). There are also some miscellaneous electrolytic caps for filtering near the regulators. For the CPU reset switch, I used one of those ubiquitous through-hole tactile switches. For the CPU clock, I started with a FOX 55.8MHz 4-pin oscillator I had laying around from a grab-bag electronic component purchase. The main CPU is where things get interesting. Before this, I had little to zero knowledge of surface mount soldering. I definitely had zero knowledge to create a PCB layout. So, I ended up using a SchmartBoard TQFP100 to through-hole adapter board. SchmartBoards use an interesting method in which the trace for each SMT pin is actually a trench. Once the part is in place, you simply slide your fine soldering iron tip along the trench. Just don’t try it with one of those wood burner type irons. Once the TQFP100 package is in place, the only difficulty is keeping track of which pins on the package map to which pins on the adapter board. I ended using the continuity function on my multimeter a lot (just to double-check of course).

With the addition of a 20-pin JTAG header, it was actually possible to test the board. Most people use OpenOCD to talk JTAG with their ARM systems, but I could not make OpenOCD work at the time. I actually ended up using OCD Commander by Macraigor Systems. It’s a simple command-line interface for running JTAG commands–poking memory, blinking an LED manually, writing to the flash, etc. I created a simple ARM assembly program that would blink an attached LED indefinitely, assembled it with the GNU ARM toolchain, and poked it into memory using OCD Commander. That was an epic moment! After that, I added an SST 256KB NOR flash (I honestly have no memory of where I got that part). The SST flash part was very easy to implement though, as it did not require a separate programming voltage like some flash parts at the time. So, I could actually erase and reprogram the flash using the external memory interface alone.

Video Board

Gamesphere video board front side Even though the audio DAC is on the CPU board, I put the audio RCA connectors on the video board, so they are next to the composite video connector.
Gamesphere video board back side I never did get the SD card interface working. The AT91R40008 doesn’t have a SPI module, so I attempted to bit-bang the MMC protocol.

The Gamesphere’s video generation system uses a brute-force approach. In the pictures above, the TQFP100 package on another SchmartBoard is a CY7C009V dual-port asynchronous SRAM. With 128KB of dual-ported RAM, the CPU can write to one video buffer while the video board is drawing the other frame to the screen. The Gamesphere outputs 224×240 8 bits-per-pixel video to VGA. Each frame uses 53,760 bytes of RAM, under half of the dual-port RAM. To flip buffers, a GPIO pin on the CPU flips the highest-order bit on the dual-port RAM address lines. From the CPU’s side of the video interface, the screen appears as a bitmap. The CPU can write a pixel color at a specific address, flip the frame buffer, and the pixel will show up on the screen. If the CPU does not flip buffers during the vertical retrace, artifacts can show up on screen. So, the video board also provides a vertical synchronization interrupt to the host CPU.

On the other side of the dual-port RAM, a PIC18F4685 microcontroller counts addresses to the dual-port RAM according to the VGA timing specification. Using 640×480 VGA, each pixel of the Gamesphere’s framebuffer covers a 2×2 block of pixels on the screen. The PIC microcontroller runs an assembly program with clock-cycle-counted loops to increment the addresses to its side of the dual-port RAM. The data lines from the RAM are fed into R2R ladder DACs. Red has a 3-bit DAC, green has a 3-bit DAC, and blue has a 2-bit DAC. A few of NPN signal transistors are used to pull all the color lines to a blanking level during syncs pulses and blanking intervals. The final result still has some artifacts, I think due to either imperfections in my video timing code, or the dual-port RAM switching banks as I count addresses. I also attempted to generate composite video output using an AD725 RGB to NTSC/PAL converter IC. I could not reach the correct video timing, at least with the TV I had available for testing. Although, you can see a correct looking waveform in the oscilloscope below.

NTSC video waverform on an analog scope The display is triggered on the synchronization pulse in the center. NTSC video as a front-porch (low voltage level), a synch pulse (zero level), and a back porch (low level). Video data follows the back porch. Since NTSC is backward compatible, color data is actually modulated onto the existing B&W signal using phase shift modulation. To create a reference phase, the back-porch includes the ‘color burst’, which you can see right after the synch pulse in this image.

Input Board

I don’t have any better photos the the input board than the overview photo at the beginning of this post. It’s not a terribly exciting board anyway. It just has a DB9 connector that I used for connecting to an N64 controller. I actually modified the N64 controller itself to have a DB9 connector (much easier to find parts for). The board itself has on PIC micrcontroller (18F series again, free samples!) that talks to the main CPU over UART. The input controller runs a program that queries the N64 controller using the obscure and precisely timed protocol.

More photos

Gamesphere video not working so well The video board did not work correctly at first, and it turned out to be very difficult to debug since different brands of monitors locked better/worse onto poorly timed VGA signals.
Gamesphere video kind of working This photo shows some of the artifacts that a poorly timed signal can generate. You can visibly see the time the screen takes to lock onto the video signal.
My workbench at the time of Gamesphere My workbench at the time; you can see the PIC and JTAG programmers in the middle of the desk.
Comments
Copyright © 2017 Craig J Bishop