mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-05-03 06:29:28 +00:00
172 lines
6.5 KiB
C++
172 lines
6.5 KiB
C++
/* unibusdevice.hpp: abstract device with interface to unibusadapter
|
|
|
|
Copyright (c) 2018, Joerg Hoppe
|
|
j_hoppe@t-online.de, www.retrocmp.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
12-nov-2018 JH entered beta phase
|
|
*/
|
|
|
|
#ifndef _UNIBUSDEVICE_HPP_
|
|
#define _UNIBUSDEVICE_HPP_
|
|
|
|
#include <stdint.h>
|
|
#include <pthread.h>
|
|
|
|
#include "device.hpp"
|
|
|
|
#include "iopageregister.h"
|
|
#include "priorityrequest.hpp"
|
|
|
|
// forwards
|
|
class unibusdevice_c;
|
|
|
|
typedef struct unibusdevice_register_struct {
|
|
// backlink
|
|
unibusdevice_c *device;
|
|
char name[40]; // for display
|
|
unsigned index; // # of register in device register list
|
|
uint32_t addr; // unibus address
|
|
// so addr = device_base_addr + 2 * index
|
|
|
|
/*** link into PRU shared area ***/
|
|
// only for access by unibusadapter
|
|
// Restauration of shared_register->value IS NOT ATOMIC against device logic threads.
|
|
// Devices must use only reg->active_dati_flipflops !
|
|
volatile iopageregister_t *shared_register;
|
|
uint8_t shared_register_handle;
|
|
|
|
/*** static setup information ***/
|
|
// "Active=true": PRU generates an event after DATI and/or DATO on that register
|
|
// (context switch to ARM: slow!)
|
|
// "Active=false": PRU handles register access with simple data value like a memory cell.
|
|
// (fast)
|
|
// "active_on_dato" is true for "command" registers, which
|
|
// start controller actions (like "CMD+GO" in RL11 and similar disk controllers
|
|
// "active_on_dati" is true, if read with DATI clears a bit,
|
|
// or if register is port to a value sequence.
|
|
// Status register should be not "active", as they are polled in loops
|
|
// But sometimes reading status clears an INTR flag.
|
|
// controllers "on_after_register_access" is called then after
|
|
// UNIBUS access to the register
|
|
bool active_on_dati; // call on_after_register_access() on DATI
|
|
bool active_on_dato; // call on_after_register_access() on DATO
|
|
uint16_t reset_value;
|
|
uint16_t writable_bits;
|
|
|
|
/*** dynamic state ***/
|
|
// if register is "active": flipflops behind address
|
|
// active registers have different latches for DATI and DATO cycles,
|
|
// and represent status/command information.
|
|
volatile uint16_t active_dati_flipflops; // latch acessed by DATI
|
|
volatile uint16_t active_dato_flipflops;
|
|
} unibusdevice_register_t;
|
|
|
|
class unibusdevice_c: public device_c {
|
|
public:
|
|
static unibusdevice_c *find_by_request_slot(uint8_t priority_slot) ;
|
|
|
|
private:
|
|
// setup address tables, also in shared memory
|
|
// start both threads
|
|
void install(void);
|
|
|
|
void uninstall(void);bool is_installed() {
|
|
return (handle > 0);
|
|
}
|
|
|
|
public:
|
|
uint8_t handle; // assigned by "unibus.adapter.register
|
|
|
|
// !!! slot, vector, level READONLY. If user should change,
|
|
// !! add logic to update dma_request_c and intr_request_c
|
|
|
|
// 0 = not "Plugged" in to UNIBUS
|
|
parameter_unsigned_c base_addr = parameter_unsigned_c(this, "base_addr", "addr", true, "",
|
|
"%06o", "controller base address in IO page", 18, 8);
|
|
parameter_unsigned_c priority_slot = parameter_unsigned_c(this, "slot", "sl", true, "",
|
|
"%d", "backplane slot #, interrupt priority within one level, 0 = next to CPU", 16,
|
|
10);
|
|
// dump devices without buffers toward CPU, smart buffering devices other end
|
|
|
|
parameter_unsigned_c intr_vector = parameter_unsigned_c(this, "intr_vector", "iv", true, "",
|
|
"%03o", "interrupt vector address", 9, 8);
|
|
parameter_unsigned_c intr_level = parameter_unsigned_c(this, "intr_level", "il", true, "",
|
|
"%o", "interrupt bus request level: 4,5,6,7", 3, 8);
|
|
|
|
// DEC defaults as defined by device type
|
|
uint32_t default_base_addr;
|
|
uint8_t default_priority_slot;
|
|
uint8_t default_intr_level;
|
|
uint16_t default_intr_vector;
|
|
|
|
// requests in use
|
|
std::vector<dma_request_c *> dma_requests;
|
|
std::vector<intr_request_c *> intr_requests;
|
|
|
|
void set_default_bus_params(uint32_t default_base_addr, unsigned default_priority_slot,
|
|
unsigned default_intr_vector, unsigned default_intr_level);
|
|
|
|
// controller register data as pointer to registers in shared PRU RAM
|
|
// UNIBUS addr of register[i] = base_addr + 2*i
|
|
// !! devices have always sequential register address range !!
|
|
unsigned register_count;
|
|
unibusdevice_register_t registers[MAX_REGISTERS_PER_DEVICE];
|
|
|
|
unsigned log_channelmask; // channelmask for DEBUG logging
|
|
// device is the log channel. one of logger::LC_*
|
|
|
|
unibusdevice_c();
|
|
virtual ~unibusdevice_c(); // class with virtual functions should have virtual destructors
|
|
|
|
bool on_param_changed(parameter_c *param) override;
|
|
|
|
// reset device
|
|
// virtual void init() override ;
|
|
|
|
// access the value of a register in shared UNIBUS PRU space
|
|
void set_register_dati_value(unibusdevice_register_t *device_reg, uint16_t value,
|
|
const char *debug_info);
|
|
uint16_t get_register_dato_value(unibusdevice_register_t *device_reg);
|
|
void reset_unibus_registers();
|
|
|
|
unibusdevice_register_t *register_by_name(string name);
|
|
unibusdevice_register_t *register_by_unibus_address(uint32_t addr);
|
|
|
|
// callback to be called on controller register DATI/DATO events.
|
|
// must ACK mailbox.event.signal. Asynchron!
|
|
// May not generate direct INTR or DMA.
|
|
// INTR/DMA as task to separate INTR/DMA scheduler
|
|
// -> orders INTR/DMA of different devices, wait for UNIBUS idle.
|
|
virtual void on_after_register_access(unibusdevice_register_t *device_reg,
|
|
uint8_t unibus_control) = 0;
|
|
// communication between on_after_register_access() and device_c::worker()
|
|
// see pthread_cond_wait()* examples
|
|
pthread_cond_t on_after_register_access_cond = PTHREAD_COND_INITIALIZER;
|
|
pthread_mutex_t on_after_register_access_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
void log_register_event(const char *change_info, unibusdevice_register_t *changed_reg);
|
|
|
|
char *get_unibus_resource_info(void) ;
|
|
|
|
};
|
|
|
|
#endif
|