[platform][rosco-m68k] Add port to the Rosco M68k board

Port to the really neat 68010 based board at https://rosco-m68k.com/

Port Features:
-10Mhz 68010
-1MB ram
-Dual UART + timer implemented as a 68c681 chip
-timer running at 1Khz, UART A for console
-interrupt driven RX support

Some amount of extending of the 68k exceptinon code was needed to
support the autovectored irqs that the 68681 uart uses. Added build
system support for 68010.
This commit is contained in:
Travis Geiselbrecht
2022-04-12 00:25:12 -07:00
parent bce9599d80
commit 49644a2c39
17 changed files with 626 additions and 87 deletions

View File

@@ -96,9 +96,10 @@ enum handler_return m68k_platform_irq(uint8_t m68k_irq) {
LTRACEF("m68k irq vector %d\n", m68k_irq);
// translate m68k irqs to pic numbers
// incoming IRQs are from 0x19-0x1f (autovectored 1 - 7 on the cpu)
int pic_num;
if (likely(m68k_irq >= 1 && m68k_irq <= 6)) {
pic_num = m68k_irq - 1;
if (likely(m68k_irq >= 0x19 && m68k_irq <= 0x1f)) {
pic_num = m68k_irq - 0x19;
} else {
panic("unhandled irq %d from cpu\n", m68k_irq);
}

View File

@@ -3,6 +3,7 @@ LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
ARCH := m68k
M68K_CPU := 68040
LK_HEAP_IMPLEMENTATION ?= dlmalloc
MODULE_DEPS += lib/cbuf

238
platform/rosco-m68k/duart.c Normal file
View File

@@ -0,0 +1,238 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "platform_p.h"
#include <assert.h>
#include <lk/err.h>
#include <lk/debug.h>
#include <lk/reg.h>
#include <lk/trace.h>
#include <lib/cbuf.h>
#include <kernel/debug.h>
#include <kernel/thread.h>
#include <platform/interrupts.h>
#include <platform/rosco-m68k.h>
#include <platform/timer.h>
#include <platform.h>
#define LOCAL_TRACE 0
// driver for a 68c681 acting as a dual uart and a system timer and a few gpios
// ticks in units of ms
static volatile uint32_t ticks;
// periodic timer callback stuff
static platform_timer_callback t_callback;
static void *t_arg;
static lk_time_t t_next_periodic_tick;
static lk_time_t t_periodic_interval;
// uart stuff
#define RXBUF_SIZE 128
static char uart_rx_buf_data[RXBUF_SIZE];
static cbuf_t uart_rx_buf;
static volatile uint8_t * const DUART_BASE = (void *)0xf00001;
// registers, swizzled according to
// https://github.com/rosco-m68k/rosco_m68k/blob/develop/code/shared/rosco_m68k_public.asm
enum {
DUART_REG_MR1A_RW = 0x00, // mode register, channel A
DUART_REG_SRA_R = 0x01, // status register, channel A
DUART_REG_CSRA_W = 0x01, // clock select register, channel A
DUART_REG_MISR_R = 0x02, // masked interrupt status register
DUART_REG_CRA_W = 0x02, // command register, channel A
DUART_REG_RHRA_R = 0x03, // rx holding register, channel A
DUART_REG_THRA_W = 0x03, // tx holding register, channel A
DUART_REG_IPCR_R = 0x04, // input port config register
DUART_REG_ACR_W = 0x04, // auxillary control register
DUART_REG_ISR_R = 0x05, // interrupt status register
DUART_REG_IMR_W = 0x05, // interrupt mask register
DUART_REG_CTU_RW = 0x06, // counter timer, upper byte
DUART_REG_CTL_RW = 0x07, // counter timer, lower byte
DUART_REG_MR1B_RW = 0x08, // mode register, channel B
DUART_REG_SRB_R = 0x09, // status register, channel B
DUART_REG_CSRB_W = 0x09, // clock select register, channel B
DUART_REG_CRB_W = 0x0a, // command register, channel B
DUART_REG_RHRB_R = 0x0b, // rx holding register, channel B
DUART_REG_THRB_W = 0x0b, // tx holding register, channel B
DUART_REG_IVR_RW = 0x0c, // interrupt vector register
DUART_REG_IP_R = 0x0d, // input port
DUART_REG_OPCR_W = 0x0d, // output port configuration register
DUART_REG_SCC_R = 0x0e, // start counter/timer command
DUART_REG_STC_R = 0x0f, // stop counter/timer command
DUART_REG_SOPBC_W = 0x0e, // set output port bits command
DUART_REG_COPBC_W = 0x0f, // clear output port bits command
};
// save a copy of whatever IMR was set to before
static uint8_t cached_imr;
static void write_reg(uint reg, uint8_t val) {
DUART_BASE[reg * 2] = val;
}
static uint8_t read_reg(uint reg) {
return DUART_BASE[reg * 2];
}
void duart_early_init(void) {
// clear all IRQs
cached_imr = 0;
write_reg(DUART_REG_IMR_W, cached_imr);
// Set the IRQ vector to 0x45
write_reg(DUART_REG_IVR_RW, 0x45);
// TODO: set up UARTA again
// for now assume it's already configured
// set up a periodic counter at 1khz
read_reg(DUART_REG_STC_R); // stop the counter
// compute the counter
uint16_t count = 3686400 / 2 / 1000;
write_reg(DUART_REG_CTL_RW, count & 0xff);
write_reg(DUART_REG_CTU_RW, (count >> 8) & 0xff);
// set timer mode
write_reg(DUART_REG_ACR_W, (0b110 << 4)); // timer mode, X1/CLK
// start timer
read_reg(DUART_REG_SCC_R); // start counter
// unmask irq
cached_imr = (1<<3); // counter #1 ready
write_reg(DUART_REG_IMR_W, cached_imr);
}
void duart_init(void) {
// finish uart init to get rx going
cbuf_initialize_etc(&uart_rx_buf, RXBUF_SIZE, uart_rx_buf_data);
// enable uart RX irq
cached_imr |= (1<<1); // RXRDY/FFULLA
write_reg(DUART_REG_IMR_W, cached_imr);
}
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
LTRACEF("cb %p, arg %p, interval %u\n", callback, arg, interval);
t_callback = callback;
t_arg = arg;
t_periodic_interval = interval;
t_next_periodic_tick = current_time() + interval;
return NO_ERROR;
}
enum handler_return duart_irq(void) {
enum handler_return ret = INT_NO_RESCHEDULE;
uint8_t isr = read_reg(DUART_REG_ISR_R);
if (likely(isr & (1<<3))) { // counter #1 ready
ticks++;
// ack the timer hardware
read_reg(DUART_REG_STC_R);
// call back the registered timer
if (likely(t_callback)) {
lk_time_t now = current_time();
if (unlikely(now >= t_next_periodic_tick)) {
ret = t_callback(t_arg, now);
t_next_periodic_tick += t_periodic_interval;
}
}
}
if (isr & (1<<1)) { // RXRDY/FFULLA
uint8_t status = read_reg(DUART_REG_SRA_R);
if (status & (1<<0)) { // RXRDY
if (status & (0b111 << 5)) { // any of break, framing, or parity error
// consume this byte
__UNUSED volatile uint8_t hole = read_reg(DUART_REG_RHRA_R);
} else {
char c = read_reg(DUART_REG_RHRA_R);
cbuf_write_char(&uart_rx_buf, c, false);
ret = INT_RESCHEDULE;
}
}
}
return ret;
}
void platform_dputc(char c) {
if (c == '\n') {
platform_dputc('\r');
}
// spin while TXRDY is clear
while ((read_reg(DUART_REG_SRA_R) & (1 << 2)) == 0) // TXRDY
;
write_reg(DUART_REG_THRA_W, c);
}
int platform_dgetc(char *c, bool wait) {
return cbuf_read_char(&uart_rx_buf, c, wait);
}
int platform_pgetc(char *c, bool wait) {
for (;;) {
uint8_t status = read_reg(DUART_REG_SRA_R);
if (status & (1<<0)) { // RXRDY
if (status & (0b111 << 5)) { // any of break, framing, or parity error
// consume this byte
__UNUSED volatile uint8_t hole = read_reg(DUART_REG_RHRA_R);
continue;
}
*c = read_reg(DUART_REG_RHRA_R);
return 1;
}
if (wait) {
continue;
}
break;
} while (0);
return 0;
}
lk_bigtime_t current_time_hires(void) {
// TODO: look at the current countdown registers
return ticks * 1000ULL;
}
lk_time_t current_time(void) {
return ticks;
}
void target_set_debug_led(unsigned int led, bool on) {
uint8_t bit = 0;
switch (led) {
case 0:
bit = 5; // green LED
break;
case 1:
bit = 3; // red LED
break;
}
if (on) {
write_reg(DUART_REG_SOPBC_W, (1<<bit));
} else {
write_reg(DUART_REG_COPBC_W, (1<<bit));
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
// TODO: add for rosco-m68k
// Top level #defines for the 68k-virt machine in qemu 6.0
//
// From qemu/hw/m68k/virt.c
/*
* 6 goldfish-pic for CPU IRQ #1 to IRQ #6
* CPU IRQ #1 -> PIC #1
* IRQ #1 to IRQ #31 -> unused
* IRQ #32 -> goldfish-tty
* CPU IRQ #2 -> PIC #2
* IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32
* CPU IRQ #3 -> PIC #3
* IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64
* CPU IRQ #4 -> PIC #4
* IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96
* CPU IRQ #5 -> PIC #5
* IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128
* CPU IRQ #6 -> PIC #6
* IRQ #1 -> goldfish-rtc
* IRQ #2 to IRQ #32 -> unused
* CPU IRQ #7 -> NMI
*/
#define NUM_PICS 6
#define NUM_IRQS (NUM_PICS * 32) // PIC 1 - 6
#define PIC_IRQ_TO_LINEAR(pic, irq) (((pic) - 1) * 32 + ((irq) - 1))
#define GOLDFISH_TTY_IRQ PIC_IRQ_TO_LINEAR(1, 32) // PIC 1, irq 32
#define GOLDFISH_RTC_IRQ PIC_IRQ_TO_LINEAR(6, 1) // PIC 6, irq 1
#define PIC_IRQ_BASE(num) (8 + (num - 1) * 32)
#define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1)
//#define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], (pic_irq - 8) % 32))
#define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */
#define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */
#define VIRT_GF_PIC_NB 6
/* 2 goldfish-rtc (and timer) */
#define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */
#define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */
#define VIRT_GF_RTC_NB 2
/* 1 goldfish-tty */
#define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */
#define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */
/* 1 virt-ctrl */
#define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */
#define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */
/*
* virtio-mmio size is 0x200 bytes
* we use 4 goldfish-pic to attach them,
* we can attach 32 virtio devices / goldfish-pic
* -> we can manage 32 * 4 = 128 virtio devices
*/
#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */
#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */
#define NUM_VIRT_VIRTIO 128

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2018 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/err.h>
#include <lk/reg.h>
#include <lk/trace.h>
#include <kernel/thread.h>
#include <platform.h>
#include <platform/interrupts.h>
#include <platform/debug.h>
#include <platform/timer.h>
#include <platform/rosco-m68k.h>
#include <sys/types.h>
#if WITH_LIB_MINIP
#include <lib/minip.h>
#endif
#include <kernel/novm.h>
#if WITH_LIB_CONSOLE
#include <lib/console.h>
#endif
#include "platform_p.h"
#define LOCAL_TRACE 0
extern uint8_t __bss_end;
void platform_early_init(void) {
duart_early_init();
dprintf(INFO, "ROSCO-M68K: firmware structure at 0x400 - 0x41f:\n");
hexdump((void *)0x400, 0x20);
uint32_t cpu_info = *(uint32_t *)0x41c;
printf("cpu family %u speed %u\n", cpu_info >> 29, cpu_info & 0x1fffffff);
// TODO: probe memory
// TODO: consider using firmware struct left around 0x400
uint32_t membase = 0x0;
uint32_t memsize = 0x100000; // 1MB
dprintf(INFO, "ROSCO-M68K: memory base %#x size %#x\n", membase, memsize);
novm_add_arena("mem", membase, memsize);
// build a table of illegal instructions around 0 to try to catch bad branches
volatile uint16_t *ptr = 0;
for (int i = 0; i < 256; i++) {
ptr[i] = 0x4afa; // undefined opcode
}
}
void platform_init(void) {
duart_init();
}
enum handler_return m68k_platform_irq(uint8_t irq) {
LTRACEF("irq %u\n", irq);
switch (irq) {
case 0x45: // DUART irq
return duart_irq();
default:
panic("unhandled platform irq %u\n", irq);
}
return INT_NO_RESCHEDULE;
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2018 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdbool.h>
void duart_early_init(void);
void duart_init(void);
enum handler_return duart_irq(void);

View File

@@ -0,0 +1,32 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
ARCH := m68k
M68K_CPU := 68010
LK_HEAP_IMPLEMENTATION ?= dlmalloc
MODULE_DEPS += lib/cbuf
#MODULE_SRCS += $(LOCAL_DIR)/goldfish_rtc.c
#MODULE_SRCS += $(LOCAL_DIR)/goldfish_tty.c
#MODULE_SRCS += $(LOCAL_DIR)/pic.c
MODULE_SRCS += $(LOCAL_DIR)/duart.c
MODULE_SRCS += $(LOCAL_DIR)/platform.c
MEMBASE ?= 0x00002000 # 8k. Just off the end of firmware reserved areas
MEMSIZE ?= 0x00100000 # 1MB
# relocate ourself from the load address (0x40000)
GLOBAL_DEFINES += ARCH_DO_RELOCATION=1
# we can revert to a poll based uart spin routine
GLOBAL_DEFINES += PLATFORM_SUPPORTS_PANIC_SHELL=1
# we will find the memory size by probing it
GLOBAL_DEFINES += NOVM_DEFAULT_ARENA=0
# we will find the memory size by probing it
GLOBAL_DEFINES += TARGET_HAS_DEBUG_LED=1
include make/module.mk