Go to file
alexis a6198d5887 Mention the ICM68M 2022-04-24 14:14:55 -06:00
gen Doc cleanup 2022-04-24 13:33:18 -06:00
icm68k Mention the ICM68M 2022-04-24 14:14:55 -06:00
misc Doc cleanup 2022-04-24 13:33:18 -06:00
.gitignore Doc cleanup 2022-04-24 13:33:18 -06:00
.gitmodules Rev 1 2022-01-04 18:06:56 -07:00
README.md Mention the ICM68M 2022-04-24 14:14:55 -06:00



This project is IN DEVELOPMENT! All promises made below are lies.

ICM68K (Instrument Control Module, 68k) is an embeddable M68010 computer, intended for controlling test equipment (or whatever else you want to control!). Its features include:

  • Motorola M68010 CPU running at 10 MHz
  • Optional memory-mapped M68881 FPU running at 10 MHz
  • Up to 13 MB application RAM plus 512 kB system memory
  • A memory protection unit supporting up to 127 user processes with read, write, and execute control
  • Dual-channel UART with hardware flow control on one channel
  • Dual-channel SPI (one for on-board peripherals, one for user) operating at up to fSCK = 10 MHz
  • Onboard micro SD card slot
  • Eight user GPIOs with interrupts
  • Real-time clock with alarm and system tick interrupts
  • Beeper with hardware volume control and frequency generation

Note that this is a large, very featureful and somewhat expensive board. See the ICM68M for a budget option.

3D render

Top view

Bottom view

Memory map

First Last Size Description
000000 07FFFF 512 kB Supervisor/OS ROM/RAM
080000 0DFFFF 384 kB Not mapped
0E0000 0E3FFF 16 kB DUART
0E4000 0E7FFF 16 kB SPI
0E8000 0FBFFF 80 kB Not mapped
0FC000 0FFFFF 16 kB FPU
100000 1FFFFF 1024 kB System bus
200000 FFFFFF 14366 kB User RAM


The M68010 CPU always boots from a reset vector located at address 000004. It is the responsibility of the memory/debug card to ensure there is a valid vector at this location at reset. Debug cards implement RAM in this region and provide a supervisor microcontroller to preload it with valid data; the simple memory card will likely map a flash device here. Note that unlike the M68000, the M68010 provides a vector base register, so no hardware remapping is required to allow both a valid reset vector on boot and runtime rewritable vectors.


The ICM68K provides interrupts in multiple layers; the board support package for the A6 operating system will decompose these into a flat set of software interrupts.

First level (M68k hardware interrupts)

There is no priority encoder generating the IPL signals to the CPU; instead, each signal is used independently as an IRQ line, with autovectoring always generated by the bus control unit. Therefore, each ISR may map to multiple vectors — as it clears its own interrupt, more interrupts will become visible.

Autovector Vector number ISR
1 25 OS tick
2 26 DUART
3 27 DUART
4 28 Debug
5 29 Debug
6 30 Debug
7 31 Debug

Note that autovector 7 is a non-maskable interrupt, so if the debug interrupt is used, this ISR must not interfere in any way with OS function.

Second level (DUART interrupts)

A second level of interrupts is provided by the DUART on pins IP1 (COM1_CTS), IP2 (nIRQ_GPIO), and IP3 (nIRQ_RTC). These interrupts may be decoded and acknowledged by a read to the interrupt status register in the DUART; see the 68681 datasheet for more information.

Note: the schematic shows IP4 as nIRQ_SPI. However, this pin is not interrupt capable. A future revision could move it to IP0, but interrupt functionality is not generally required on the SPI transceiver so the current plan is to leave this unfixed.

Note: nIRQ_RTC is a general-purpose RTC IRQ which may be used for the alarm functionality. The OS tick interrupt, also generated by the RTC, is routed directly to the CPU.

Third level (GPIO interrupts)

Up to eight user interrupts are provided by the MCP23S08 GPIO chip, whose interrupt pin connects to the DUART on IP2.

OS tick interrupt

The OS tick interrupt is handled in a special way. It is generated by the real-time clock, which outputs a square wave. This is converted into an interrupt pulse by an XOR gate combining it with a GPIO. When the OS tick ISR executes, it should toggle the state of pin OP5 (RTC_IRQPOL) on the DUART to acknowledge the interrupt.

Because the RTC state is not cleared by system reset, this interrupt could be active at boot.


Debug/memory card

There is no onboard system memory; both supervisor and user regions are provided by a mezzanine card. This interface is designed to provide the option of a hardware debugger (which is at the time of writing the only mezzanine card that has been built).

In addition to the normal memory interface, this mezzanine connector provides:

  • System clock (10 MHz)
  • Reset in/out
  • Breakpoint-detected signal from the bus control unit
  • Separate chip selects for supervisor and user spaces
  • Bus mastering signals
  • One IRQ line
  • Bus timeout inhibit control
  • Access to the CPU function code bits
  • 5V and 3.3V power; 5V is bidirectional (the debug card allows powering the ICM68K via a USB port)

Bus control unit

The bus control unit is a CPLD, named OAK, providing address decode, clock generation, and part of the memory protection unit functionality.

Memory protection unit

The memory protection unit (MPU) on the ICM68K allows the operating system to apply memory protection to up to 127 user-mode processes. Within each process, every 2 KB section of address space can be assigned a permission mode. Permission violation when in user mode triggers a bus error.

To select the active process, write the process ID (0 to 127, with 0 intended for the kernel) to the task select register, using a byte-size write to any even-numbered address with function code 3 (use movec to load 3 into the DFC register, then use the moves instruction to perform the write with this function code). The high bit functions as an active-low "bypass" bit and should be set to 1 (currently, A6 does not make use of this bypass functionality).

To write to the protection table for a process, first select that process as above in the task select register. Then, write a control byte to the first odd-numbered address in the block of four 2 KB pages, using function code 3 as above. For example, the control byte for the page containing address 069420 is at (0x069420 & 0xFFE000) | 1 or 0x068001. This control byte contains permission codes for all four pages in the block, as follows:

 msb                         lsb
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  |___|   |___|   |___|   |___|
    |       |       |       |
    |       |       |       |____ page n + 0
    |       |       |
    |       |       |____________ page n + 1
    |       |
    |       |____________________ page n + 2
    |____________________________ page n + 3

Permission modes are:

  • 00: NO_ACCESS: all access denied
  • 01: READ_ONLY: data reads allowed, writes and code reads denied
  • 10: READ_WRITE: data reads/writes allowed, code reads denied
  • 11: READ_EXEC: data and code reads allowed, writes denied

Note that memory may not allow both execution and writing at the same time. Memory protection does not apply to supervisor-mode accesses or while bus mastering is active.

The memory protection table may be read back by performing a FC=3 read to the same address (note that you must then also place 3 in the SFC control register). However, the task select register cannot be read back.

Hint: if the kernel needs to validate whether the current process has permission to access a location, it is much faster to simply perform this access with FC=1 (userspace data) and let the MPU block it than it is to read back the protection table and compute whether the access would be permitted. This requires the support of the bus error vector handler.


The 68681 dual UART provides two UARTs, one timer, and several inputs and outputs.

  • UART 0 provides only TXD and RXD.
  • UART 1 provides TXD, RXD, RTS, and CTS. It is intended for use with the system and bootloader console.
  • The timer can be used to control the beeper or as a baud rate generator or periodic interrupt generator. (Note that for system ticks, the intended source is the RTC.)
  • The following IOs are used:
    • OP0: SPI chip select for the user GPIO
    • OP1: UART 1 RTS
    • OP2: up/down control for the beeper loudness adjustment
    • OP3: timer output; provides square wave for beeper
    • OP4: SPI chip select for the SD card
    • OP5: RTC IRQ polarity/acknowledge
    • OP6: SPI chip select for RTC
    • OP7: beeper loudness adjustment chip select and status LED (see that section)
    • IP0: not connected
    • IP1: UART 1 CTS (interrupt capable)
    • IP2: GPIO IRQ (interrupt capable)
    • IP3: RTC IRQ (interrupt capable; separate from OS tick)
    • IP4: SPI IRQ/flag (bug: NOT interrupt capable)
    • IP5: SD card sense


The SPI transceiver is a CPLD, named ASH, providing two SPI channels with configurable mode and frequency up to 10 MHz. (TODO: add documentation on this)

Beeper and status LED

The onboard beeper is controlled by the DUART. To set the loudness, drive the up/down control (OP2) and chip select (OP7) per the digital potentiometer datasheet here (MCP4011). The potentiometer only permits relative adjustment; to set an absolute value, send enough "up" or "down" commands to saturate it at the top or bottom first.

The beeper output OP3 should be held high when not in use.

The LED is multiplexed with the loudness chip select, with a bypass capacitor across it to prevent flashing while adjusting loudness. To control the LED, simply drive this pin to a constant level. Additional pulses may be sent to the potentiometer to compensate for the resulting one-step change in loudness that will be caused by the LED toggle; alternatively, always set the loudness to an absolute level as described above when emitting a beep.

The A6 board support pack for the ICM68K will provide a driver to handle this and present demultiplexed LED and beeper devices to userspace.

Debug console

By convention, a memory mapped debug console is provided by the debug card. The memory interface control hardware on that card intercepts writes to the supervisor region and uses this to implement a control interface.

The control interface takes the form of a single 16-bit memory mapped register located at address 07FFFE, the last address in supervisor space. Each write to this register is a command, with the command index in the upper byte and the argument in the lower byte. Commands are as follows:

  • TRANSMIT (0): send the argument on the debug console.
  • SET_RXBUF_3 (0x10): set byte 3 (MSB) of the receive buffer address
  • SET_RXBUF_2 (0x11): set byte 2 of the receive buffer address
  • SET_RXBUF_1 (0x12): set byte 1 of the receive buffer address
  • SET_RXBUF_0 (0x13): set byte 0 (LSB) of the receive buffer address
  • SET_RXLEN_1 (0x14): set byte 1 (MSB) of the receive buffer length
  • SET_RXLEN_0 (0x15): set byte 0 (LSB) of the receive buffer length
  • EN_DIS_RX (0x16): enable (arg is true) or disable (arg is false) receive
  • SET_VECTOR (0x17): set vector number for RX interrupt (see below)
  • IACK (0x18): acknowledge the interrupt (see below)

The receive buffer is a ring buffer the operating system must allocate in RAM. It must be word-aligned. The first word is the head index, the second word is the tail index, and the rest is data. Once allocated, write the address of the beginning of the ring buffer using the SET_RXBUF_n commands, write the length of the data portion in bytes using the SET_RXLEN_n commands, then enable receive using EN_DIS_RX. As bytes are received by the debugger and passed to the operating system, the debugger will increment the tail index, and the operating system will detect head != tail, receive the bytes and increment the head index. Always use word-size accesses to ensure atomicity.

The debug console interface definition supports both full M68000 interrupt vectoring and autovectoring; the ICM68K only supports autovectoring. To enable interrupt on byte received, write any autovec vector number into the debug console's vector register to enable interrupts. When the ISR runs, you must write the IACK command to acknowledge and clear the interrupt. Beware that the ICM68K maps the IRQs such that this may be a nonmaskable interrupt if all three IRQs assert simultaneously. Use of this interrupt is generally not recommended or needed, though it may be useful to support future debug functionality.

(TODO: the control register was previously mapped at 01FFFE, not 07FFFE. The code still needs to be updated to reflect this.)

Floating point unit

The M68010 does not support coprocessor instructions, but it is possible to memory-map a M68881 floating point unit. The ICM68K provides a socket for this. Note that the A6 operating system does not presently provide any support for it.

The FPU SENSE line is not wired up. To detect presence or absence of the FPU, simply attempt to access it. Because ICM68K generates a bus error on timeout, this access will not hang if the FPU is not present. A future A6 FPU kernel module will perform this test on boot — it is not available to userspace.

System bus

This address range is for user expansion via the DIN 41612 connector. The system bus chip select line is asserted by the bus controller whenever an address in this range is accessed. If the system bus (or a section of it) is not used, it may reply with a bus error, but the onboard bus timeout generator can take care of this automaticaly.

User RAM

The last four words of RAM (FFFFFC through FFFFFF) should not be used; they may map to control registers of PSRAM on memory cards. Memory cards should ensure these all sit at the end of RAM (if using multiple chips, interleave them), and should ensure that the usable region is also aligned to the end of RAM. Memory cards must respond to accesses to the entire User RAM region; all of these detectable variations are permitted:

  • Entire address range maps to RAM (fully populated option)
  • Some segments trigger bus error
  • Some segments acknowledge, but no data is read or written
  • Some segments overlap with other segments

Because overlap (generally the cheapest way to map smaller amounts of RAM to larger address ranges) is permitted, an operating system looking to dynamically detect RAM must test by writing to a region, then scanning forward to ensure its write did not show up elsewhere.

Segments must be at least 512 kilobits (64 kB) in size.

Bus timeout generator

The ICM68K contains a bus timeout generator circuit that generates a bus error condition if the address strobe asserts for approximately 4 ms without a response. This may be disabled by the memory/debug interface by applying at least 3V, and no more than 6V, to the BTO_INHIBIT signal. This signal should otherwise float, and voltage may be applied via a diode to ensure this. Because the bus timeout generator only watches the AS signal, it will trigger for all hung accesses to any address.

PLD configurations

  • OAK: The config files for OAK, the bus control unit, may be found at TODO. OAK must be a 5V-variant ATF1502 or ATF1504 CPLD, or the Altera equivalents.

  • ASH: The config files for ASH, the SPI transceiver, may be found at TODO. ASH must be a 3.3V-variant ATF1504 CPLD, or the Altera equivalent.

Intellectual property is bullshit. This is everyone's design.

Contact me (issue reports, patches, bare board requests)

Send an email to my username at alexisvl.rocks. I suggest mentioning the project name in the subject line.