Scalar FPGA PDP-11 in Verilog


I got tired of reading about other "pdp-11 fpga recreation" projects that were either (1) private or (2) not in verilog.

If you want to reproduce the FPGA pdp-11 project look at this page.

One day I got fed up, went insane, and decided to make my own PDP-11 in verilog. I put my copyright on the code but I put it out there for anyone to look at or use. My criteria for the design was/is:

  1. Written in Verilog
  2. Fits in reasonable FPGA with <$200 development board
  3. Acts like an 11/34 or 11/44
  4. Boots RT-11 and BSD (2.9 or 2.11)
  5. Simple scalar design, direct decode, no pipeline.

I did it (mostly) but it turned out to be more difficult than I thought it would be. My thinking was that since the PDP-11/20 was done as a no-microcode, direct decode cpu, that it least it could be done.

I made a simple adapter board for the IDE drive. It turns that LVTTL drive of the FPGA (3.3v) will work fine with any modern IDE drive. Apparently most motherboards these days drive IDE drives with 3.3v.

I'll make up a real double-sided PCB shortly and sell one to anyone who wants one for $15.

I bought a small FPGA board from Digilent, the "S3BOARD" and got the "XC3S1000" upgrade. This board is perfect as it has 1mb of sram and an RS-232 port.

I then did the project in multiple phases:

  1. Behavioral model, written in "rtl like" C code (i.e. clocked data and comb. logic)
  2. Behavioral co-simulation with simh
  3. verilog RTL
  4. verilog RTL co-simulation with simh
  5. Synthesis & FPGA debugging

When I started this I was (am) also working on a "real" cpu at a real company doing much the same thing, only on a larger and more grand scale. I learned a lot from that experience and applied some of it to this project.

Behavioral Model

The behavioral model was written in C. I wanted to use an easy to write language which would allow me to have lots of debugging code. It basically models the cycles and state machine of the RTL cpu. I quickly got it to fetch and run basic instructions. But I got too adventurous too quickly and added an "TT" serial interface and an "RK" disk interface and tried to boot RT-11. It didn't work.

Behvaioral co-simulation with simh

One thing I learned from my day gig was that when creating a new cpu to run existing code it's really helpful to have a solid "golden" behavioral model which you can compare results with. So I hacked simh to allow me to run it lock step with my behavioral model and to compare the results - basically memory i/o and registers after each cycle. Needless to say this was invaluable. It allowed me to find a lot of bugs. But it still would not boot RT-11. It got a lot further, but no joy.


At one point my behavioral model sort of ran out of gas and started becoming more pain than it was worth. It didn't accurately model the clocking of data. It was very nice for flow, but it didn't really have clocks. So I converted the C code into Verilog.

The verilog took a while to get going but still largely follows the original C behavioral model. Once it was working I re-ran all my simple test cases and compared them to simh. I then ran some official DEC diagnostics: the pdp-11/34 basic instruction test and the EIS tests. These found a lot of bugs. But not all. The diags contain some pretty nasty code. I have respect for the person who wrote them.

''(aside: the thing that fueled my fire was reading about someone else running DEC diagnostics on their VHDL pdp-11. It sounded like fun. I just had to try it)''

I then spent a lot of time writing "VPI" modules in C for the verilog simulator. I'm currently using cver for most things. I write a VPI model for the RK disk, the ram and an real IDE disk. I made various modules to do different levels of simulation. One RK module does "magic" instant dma (since it is compiled with the rma module). This is not stressful to the cpu. Another RK module does real DMA, which tests the cpu/bus memory arbiter. But it's good to start simple with lower stress. In the end I just used the real RK rtl and a simple model for the SRAM and the IDE disk.

Surprisinging I could not find any simple VPI IDE module, so I wrote my own. It's it's not complete or perfect but it's good enough for basic testing and it does boot RT-11.

So, then I had a pdp-11 cpu, a "TT" serial interface with uart, an RK module with an IDE back end. But it would not boot RT-11. No joy.

I should stop for a minute and say I got a lot of inspiration from the the "POP-11" project. There idea for emulating an RK-11 drive with an IDE drive was brilliant. Unfortunately they wrote everything in an interesting language called "sfl", which is proprietary. The language is nice, however. It presents a model which is more state machine and event driven. I like the idea behind but it's closed and not verilog. So, I used their idea and their RK-11 state machine. But still no RT-11. Much much closer, however.

The cpu would synthesize (after some editing) quickly and would run in the FPGA but would not boot. I had to do some debugging of my RS-232 interface. During sim it appeared to work but I never really exerised it, so naturally it didn't work right. For a while RT-11 would boot somewhat and spew out garbage. This was due to bugs in my uart it turns out. I wrote some simple pdp-11 code to do console output and debugged it.

After that RT-11 would almost boot but die with a stack overflow. Closer. Much closer.

Since it's a fully static design, I can run the clock at any speed. I used the slide switches on the board to program a clock divider. I can run the code as slow as 1 instruction per second. This was handy for debugging as I display the PC on the seven segment LED's so I could follow the code. I used this a lot to debug the IDE disk interface. My VPI model was good but I had wiring errors on my interface board (oops) which took me a bit to debug. The RK state machine would hang but I finally found the missing wire and it started to work.

But still no RT-11 boot.

RTL co-simulation with simh

I finally broke down and did an instruction by instruction co-simulation between the rtl and simh. This took a long time and huge amounts of virtual memory (> 4gb) but uncovered two sublte bugs in byte opcode which the diags did not. Once I fixed those it would boot RT-11 to the "." prompt and I could type commands. yea!

But console input was not right. Another bug in untested code in tt_regs.v. Fix that and got a clean boot. And I could type commands! yea! much joy!

Below is a snapshot of the current verilog. I have more files to post, like the converter board pin-out and the Xilinx ISE project file. See the page (above) on reproducing the fpga. Current State:

The rtl boots 2.9BSD unix to the shell prompt in simulation!

brad@mini:~/cpus/pdp11/verif$ ../utils/graboutput/graboutput boot

: rk(0,0)rkunix

Berkeley UNIX (Rev. 2.9.1) Sun Nov 20 14:55:50 PST 1983
mem = 127680

xp ? csr 176700 vector 254 skipped:  No CSR
rk 0 csr 177400 vector 220 attached
hk ? csr 177440 vector 210 skipped:  No CSR
rl ? csr 174400 vector 160 skipped:  No CSR
rp ? csr 176700 vector 254 skipped:  No CSR
ht 0 csr 172440 vector 224 skipped:  No CSR
tm 0 csr 172520 vector 224 skipped:  No CSR
ts 0 csr 172520 vector 224 skipped:  No CSR
dh ? csr 160020 vector 370 skipped:  No CSR
dm ? csr 170500 vector 360 skipped:  No autoconfig routines
dz ? csr 160110 vector 320 skipped:  No CSR
dz ? csr 160110 vector 320 skipped:  No CSR
dn 0 csr 175200 vector 300 skipped:  No autoconfig routines
vp ? csr 177500 vector 174 skipped:  No autoconfig routines
lp ? csr 177514 vector 200 skipped:  No CSR
Erase=^?, kill=^U, intr=^C

The current cpu acts like a 11/34 with EIS and memory management. RT-11 thinks it's an 11/45/50/55 with EIS. I believe the multiply and divide, while slow, are correct.

I've completely revamped and automated the regression setup with subsystem testbenches, code tests and diags. I've added the MMU and it passes (mostly) the MMU diags. The rtl boots V6 unix in simulation, at least to the "login:" prompt. I plan to try 2.11 and RSTS V7 next.

  • RT-11 boots to command prompt.
  • V6 Unix boots to command prompt.
  • RSTS V4 boots to command prompt, but doesn't use the MMU.
  • 2.9BSD boots to shell prompt.

I have not debugged split I&D yet, but I plan to soon. I feel like I should make an MSCP disk controller, but that is not a small task.

The cpu instruction (11/34) diags and mmu diags pass, with a few minor expceptions. The problems always from from what happens when the cpu aborts an instruction for some reason. Since my cpu is not microcoded, what happens is dependant on the state machine, and this differs slightly from the 11/34 microcode.

C:\brad\pdp11\verif>..\utils\graboutput\graboutput <log


PSW     PC      SR0     SR2
170017  (3$+4)  020147  (3$)
PSW     PC      SR0     SR2     TESTNO  ERRORPC
140010  031702  020147  031702  000045  032046


Once this is done I want to make a pipelined version of the cpu. And after that a superscalar version. Just for fun. Doesn't everyone need a pipelined pdp-11? Downloads

Latest snapshot:

see the reproduce page for working download links...