In the source tree of the distribution, there are directories for the boards that I support – generally, these have the name of the board or some abbreviation of that; look at the files page or in the source tree to find these. Each board directory contains the specific files needed for that board; the generic parts are in the top level directory.
The top level source file in the hierarchy of VHDL that forms the PDP2011 systems is usually called top.vhd – which is different for each board, and thus a variant exists in each board directory. It contains the glue logic between the components surrounding the FPGA – memory, connectors, switches, and blinkenlights – and it instantiates the Unibus containing the system peripherals, the CPU and the MMU. In the instantiation of the Unibus component, you can configure many aspects of the system; what you can do with it is described below. Almost all attributes you can configure also have default values that allow you to omit anything you do not need – usually if you omit to configure something, it will not be included in the system; exceptions are those things that are likely to be required, like for instance the line clock, or those things that generally do no harm, like the blinkenlights register.
The configuration options described here are however also somewhat limited, for instance, it will not allow you to have more than 4 serial links, and also it will not allow you to have two of the same type of disk controller. If you want this, you’d have to change the unibus.vhd source – which is a bit beyond the scope of a howto, and requires you to have a deep understanding of VHDL and the structure of PDP11 systems and the PDP2011 implementation.
If you decide to have a go at making your own configuration, the easiest way is to start from a working configuration, and changing one thing at the time. And, unless you are sure you know what you’re doing, it is best not to touch anything that is not described here.

System model
Look for the line in the unibus instantiation that sets the signal called ‘modelcode’.

pdp11: unibus port map(
   modelcode => 70,

With this, you set the type of the system, and following from that, the type of CPU, MMU and whether or not an FPU will be available. Many codes are used throughout the sources, but some refer to non-Unibus systems or are not complete; the following model codes will produce working systems:

  • 3; 11/03 system, no MMU, no FPU, but including EIS and FIS
  • 20; 11/20 system, no MMU, no FPU
  • 23; 11/23 system, 18-but MMU (2-bit ACF),FPU
  • 34; 11/34 system, 18-bit MMU (2-bit ACF),FPU
  • 44; 11/44 system, 22-bit MMU (2-bit ACF),FPU,Unibus map
  • 45; 11/45 system, 18-bit MMU (3-bit ACF),FPU
  • 70; 11/70 system, 22-bit MMU (3-bit ACF),FPU,Unibus map
  • 94; 11/94 system, 22-bit MMU (2-bit ACF),FPU,Unibus map

Which system model you set makes a huge difference on the amount of FPGA resources that are needed to implement it. The 11/70 with FPU is probably the biggest in terms of resources, and 11/20 the smallest – but there is also a marked difference between 11/34 and 11/45. Besides the resource consumption, there is also the question of software support to consider when choosing a system model; not every operating system necessarily needs the biggest system. Regarding speed, all are equal – unlike the real hardware, the PDP2011 systems all run at the same speed; there is no difference in the amount of MIPS or cycles-per-instruction, regardless of which model you choose. One peculiarity is that the FPU will always compute results for double precision – and is thus potentially slower than using the FIS instructions; but note that the Unix-variants will also only use double precision.

There are several more subtle differences between the systems than suggested in this list; the original systems differed in many details, like exactly which additional instructions were available, or how the exact timing of interrupts would interact with the flow of instructions, which control registers were available to control the way the hardware would work or how it would report unusual conditions. For details, look in the cpu.vhd, mmu.vhd and cr.vhd files – this is where the main differences are implemented. In many cases, the modelcode is used in these files to set signals of the form have_X, where X is a specific attribute. As an example, whether or not the CPU will include the SOB instruction depends on the value of the have_sob signal. In the core, you will find many model codes that are not listed above, but these have not been fully tested, and may not produce a working system – most obviously those that refer to a non-Unibus system, although even that works surprisingly well for 18-bit QBUS systems like the 11/23.

By default, the FPU is included for those models that could optionally include it. This can be overridden however, by setting a signal named ‘have_fp’ in the unibus instantiation. A value of 0 will disable the FPU; a value of 1 will force its inclusion. Normally, you’d use this if you do not have enough FPGA resources to fit the FPU – it takes a lot of resources to include the FPU, and many systems include a software simulator that can replace it – even if not out-of-the-box, you can create systems that can do without the FPU hardware and still support floating point arithmetic; a good example of this is 2.11BSD. However, it’s also possible to create unlikely systems – like an 11/20 with an FPU; surprisingly, this will work with RT-11.
The following example creates an 11/70 without FPU:

pdp11: unibus port map(
   modelcode => 70,
   have_fp => 0,

Serial lines
Serial lines (and the KL-11 like controllers that drive them) are defined with the have_kl signal. It can have values from 1 up to 4. For each serial line you configure, two signals named rxN and txN must be connected to the appropriate pins of the FPGA. Also, for each link you can set a signal klN_bps; valid values range from 1200 to 230400, and the default value is 9600. You should consider that each byte input or output generates an interrupt; running a link at high speed is a lot of work for the CPU. It is not generally a good idea to go beyond 38400.
For more reliable transmission at higher speeds, the serial link controller can implement RTS/CTS signals – by default it does not. This can be enabled by setting the signal klN_rtscts to 1; if you do, you must also connect the signals rtsN and ctsN. Also, setting to 1 causes that the controller will include a receive buffer, so that bytes will not be lost if the CPU does not respond to an interrupt in time.
Finally, there is the option to force the highest order bit of each byte received and transmitted to zero. This is mostly useful for the console line of systems running older operating systems; these sometimes assume that the hardware will only handle 7 bits. Enable this by setting klN_force7bit to 1.
The following example creates two serial links, the first one intended for the system console, and one high speed link for inter-system communication:

pdp11: unibus port map(
      have_kl11 => 2,
      rx0 => rxrx0,
      tx0 => txtx0,
      kl0_force7bit => 1,
      rx1 => rxrx1,
      tx1 => txtx1,
      cts1 => cts1,
      rts1 => rts1,
      kl1_rtscts => 1,
      kl1_bps => 38400,

Line clock
The kw11-l line clock is used to generate a periodic interrupt; it is required for all systems that do some kind of multitasking. Originally, the frequency was derived from the mains frequency; hence the usual rate of 60Hz for the US, and 50Hz for Europe. In the PDP2011 systems, the default frequency for the kw11-l is 60Hz; in this case this is derived from a crystal oscillator on the FPGA board. It can also be set to 50Hz, or even 400Hz. Mostly it is advisable to leave it at the default of 60Hz, as this is supported by more software – and it saves you from having to set it in a sysgen.
The following example sets the clock frequency to 50Hz:

pdp11: unibus port map(
      kw11l_hz => 50,

Disk controllers
There are three different disk controllers: rk, rl, and rh. You can only have one of each type, and whether or not each type is included is governed by the signals have_rk, have_rl, and have_rh; setting these to 1 includes the controller, and setting to 0 disables it.
If you include a controller, the signals of the form rX_sdcard_cs, rX_sdcard_mosi, rX_sdcard_sclk, and rX_sdcard_miso must be connected to FPGA pins that are in turn connected to where the SD card cage is located on the board. Additionally, the controllers output a 4-bit vector called rX_sdcard_debug that will show whether the card can be accessed (bit 0, on for no access); whether the card is SD or SDHC (bit 1, on for SD); when the card is being read (bit 2); and when the card is being written (bit 3). It is advisable to connect the 4-bit vector to leds on the FPGA board so you can see if the card is connected and when it is actively used.
The following example includes an RH controller for an RP06 disk image:

pdp11: unibus port map(
      have_rh => 1,
      rh_sdcard_cs => sdcard_cs,
      rh_sdcard_mosi => sdcard_mosi,
      rh_sdcard_sclk => sdcard_sclk,
      rh_sdcard_miso => sdcard_miso,
      rh_sdcard_debug => sddebug,

If you go about changing the disk controllers, you also need to consider booting from these disks; see under ‘booting’ in the running howto.

The DEUNA/DELUA controller is called xu; it is a frontend for the ENC424J600 from Microchips, and most easily connected to the FPGA in the form of Digilent’s PMODNIC100. The xu controller actually contains a complete system, including unibus, CPU, microcode, line clock, and serial link – all this is hidden under the simple configuration described here. You enable the controller by setting the have_xu signal to 1. If you do, you must connect the signals xu_cs, xu_miso, xu_mosi, and xu_sck, and interface these to the appropriate pins of the ENC424J600 chip or PMODNIC100.
The following example includes an DEUNA/DELUA controller:

pdp11: unibus port map(
      have_xu => 1,
      xu_cs => xu_cs,
      xu_mosi => xu_mosi,
      xu_sclk => xu_sclk,
      xu_miso => xu_miso,

PiDP-11 console
Oscar Vermeulen’s PiDP-11 is connected by a driver component that multiplexes the signals for the LEDs, reads and debounces the switches, and decodes the rotary switches. It needs a lot of signals – best to look at one of the pre-built configurations. There is only one interesting configurable item: the value for the signal ‘paneltype’. Set this to 1 for a ‘normal’ PiDP-11; or to 2 if you have one where the rotary switches ‘turn the wrong way’. Or you can set it to 0 to disable the interface (and save a lot of room in your FPGA).


John Luke September 21, 2021 Reply

In the sample rh disk controller you say
rh_sdcard_cs => rh_cs,
Should this say
rh_sdcard_cs => sd_cs,
Also can not find any mention of
in the code.
Is this documentation up to date ?

John Luke

John Luke September 21, 2021 Reply

Correction in the previous comment I should have written
rh_sdcard_cs => sdcard_cs,
as in the cy10k configuration.

sytse September 21, 2021 Reply

Hi John

ah yes, you have found a mistake – this was from before I included several variants of the build for the cyc1000 board. Thanks, I’ll correct it!


John Luke September 22, 2021 Reply

What about have_rh_debug ?

John Luke

sytse September 22, 2021 Reply

Ah, I see what you mean. It’s been rewritten now.

Leave a Reply