All robots need sensors, computation and actuation.
The best way to get serious computation to run *The Hard Stuff* like SLAM and vision algorithms is to get a normal, off the shelf x86 PC. This gives a familiar Linux environment to do your development and deployment on. However, there is a serious problem, off the shelf motherboards do not provide a low-latency and noise tolerant connection to the outside world. USB seems like the answer, but once the bus gets busy, the latency will bite you, and Asimov help you if you connect to a motor controller via USB.
Inside the case lurks a solution, PCI Express. It's fast, and low latency, though you'll have a hard time finding a PCI-e motor controller or Lidar. That's where ROPS comes in, to act as a bridge between PCI-e and buses like CAN and RS485 that you will find on your motor controllers and sensors.
Since nearly all mobile robots need an IMU and GPS, and a Barometer is helpful, we'll include those as well.
Tentatively, we'll be using the Xilinx Artix-7 XC7A35T in the CSG325 package as the heart of ROPS. The 35T is a mid-range Artix part, and the CSG325 package bonds out the all-important GTP transceivers so that we can use PCI-e to connect to the host computer.
The sensors we plan to use are the ST LSM9DS1 IMU, ST LPS25HW barometer, and the uBlox NEO-M8T GPS.
There will be back-panel IO for connections to third party hardware such as motor controllers and additional sensors, but we haven't nailed down any details yet. Very little of the FPGA's IO is currently spoken for, so there is a lot of flexibility here. It's also likely that additional IO will be available on mezzanine or flex cable connectors inside the host computer's case.
When you're working on any kind of code, tightening up the write-compile-test loop is always helpful, especially when you're first learning. In the case of the SPI module, the most important thing to test is the timing of the SPI outputs. To do that we need to simulate the verilog and inspect the outputs. Here are the tools I used to do that.
I use neovim, but it doesn't really matter. I do highly recommend something with syntax highlighting, a robust find and replace, and cross-platform capability so you can use it everywhere you go.
This is where the magic happens, this script takes an argument in the form of the file you're currently working on, and waits for it to be saved. Once it is saved, the script compiles that file, simulates it, and updates GTKWave's output. I'm using it for verilog here, but you could replace the stuff after inotifywait with whatever would be useful for the project you're working on. This could be a compiler, linter, etc. I keep it's output in the top left pane of my workspace, so I can inspect any errors that come up.
inotifywait -e close_write $1
iverilog -o main $1
vvp main -lxt2
gconftool-2 --type string --set /com.geda.gtkwave/0/reload 0
This really is the key to the script. inotifywait exits when the file given to the script as the first argument is closed, if it was opened as writeable. That way if you cat the file, or diff it, it won't do anything. It doesn't look inside the file to see if changes were made though, it just knows the file was closed.
Icarus Verilog is a verilog synthesis and simulation tool. iVerilog compiles the source into an intermediate assembly-like source that is then executed by another part of the iVerilog toolset, vvp. Doing that outputs a .lxt file that stores the waveforms of the various wires and registers in the verilog.
In the script, the -o option to iverilog names the output file, here we just use main. Then we call vvp on main, and tell vvp to output the waveforms to a .lxt file.
When you simulate verilog, you can use printf-like statements, or you can dump the status of every line and register into a file. To look at the register dump. I'm using GTKWave. It is, in my opinion, a perfect piece of software. It does everything I need when I want to inspect the waveforms, and nothing else.
In the script, we call gconftool-2 to update the waveform. I have no idea why they chose a config tool to do this, but it works, so who am I to complain?
There's an old joke I like about programming, it goes like this:
"Programming is easy. After all, the computer does exactly what you tell it too.
Programming an FPGA is easy too. After all, it does exactly what you tell it too, in parallel.
The arty is here, and I'm re-learning digital design. It's been a while since I did it in college. Writing verilog is pretty alien for a firmware guy like me. When I started reading about HDLs, every guide and article I read said something to the effect of "verilog is not software, don't treat it like software." As you are probably doing now, I would nod at the sage advice of my predecessors. However, until I tried to actually implement something, it didn't really sink in.
Before we get started, I'd like to thank the helpful folks on freenode/##verilog for their advice, the wonderful resources at asic-world and Embedded Micro, people who take the time to answer stack overflow questions, and all the professors in the world who put their notes/slides online for free.
The first thing I want to get the hardware doing is reading from the IMU and Barometer, and spitting that data out on the UART. There are a lot of moving parts to doing that, but today we're gonna start with the SPI module. It should be noted that this is a solved problem, but I needed something to to use to learn verilog.
These two pictures tell you everything you need to know about SPI, if you stare at them long enough. The timing diagram shows the sequencing of the CS, the clock, and the data lines. The shift register diagram shows a generic layout for a simple bus. If you want to learn more about SPI, I highly recommend the Wikipedia page on the subject. It's excellent.
SPI has 4 modes, depending on the CPHA and CPOL settings. We're just gonna talk about SPI mode 0, or when both CPOL and CPHA are 0. Lets jump into the Verilog.
The reg_width parameter sets the maximum number of bits in our data registers. It's also the only parameter that can be set when the module is instantiated. In most cases, you'll want 8 bits, but 12 and 16 bit width transactions aren't terribly uncommon. It also allows for an easy way to do large continuous reads. When we get to actually talking to the sensors, we'll be using that feature to shift out all the bytes of a sensor reading at one time.
parameter reg_width = 8
These two parameters are used internally. Counter_width is maximum number of bits to shift out. The clog2 function is ceiling of log base 2 of the input. The second line is the state parameters, defining reset as 0, idle as 1, and so on.
The module's IO can be grouped as system-side and SPI-side. System-side has the reset, the clock, a transact start signal, the data to be shifted out, the data shifted in, and the number of bits to shift. SPI-side has the standard SPI connections of MOSI, MISO, CS, and SCLK (called spi_clk to reduce confusion with sys_clk). The SPI-side connections all connect to hardware pins on the FPGA.
To jump start the HDL/software side of things, we're gonna use a Digilent Arty A7. Luckily, Digilent already has a PMOD board with the ST IMU and Barometer, so that's job done and in the mail. No such board existed for the GPS module, until about 3 hours ago when I finished one. It's a very simple board that connects the GPS module to a type 2A (extended SPI) PMOD. It was also a nice way to knock the rust off my PCB layout chops before I start working on the much more complicated main board.
The module can power an active antenna, if the LNA POWER pins are shorted, or an external power supply can be connected through the LNA POWER pin closer to the sma connector. The LNA EN pin is an output to enable an external LNA. If you use a passive antenna, only C2 and C3 are required, the other passives can be omitted with no loss of function.
Pins 9 and 10 of the PMOD are connected to the Timepulse and Safeboot pins of the gps.
10 nF, 16V, 10%
Decoupling caps for VIN and for active antenna power