1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-01-30 21:32:18 +00:00
Files
livingcomputermuseum.UniBone/10.02_devices/2_src/dl11w.cpp

356 lines
9.6 KiB
C++

/* DL11W.cpp: sample UNIBUS controller with SLU & LTC logic
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
20/12/2018 djrm copied to make slu device
14/01/2019 djrm adapted to use UART2 serial port
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include "unibusadapter.hpp"
#include "unibusdevice.hpp" // definition of class device_c
#include "unibus.h"
#include "dl11w.hpp"
#include "gpios.hpp"
#include "rs232.hpp"
char buffer[BUFLEN + 1];
//-------------------------------------------------
slu_c::slu_c() :
unibusdevice_c() // super class constructor
{
//ip_host.value = IP_HOST; // not used
//ip_port.value = IP_PORT; // not used
// static config
name.value = "SLU";
type_name.value = "slu_c";
log_label = "slu";
set_default_bus_params(SLU_ADDR, SLU_VECTOR + 4, SLU_LEVEL); // base addr, intr-vector, intr level
// init parameters
// controller has some register
register_count = slu_idx_count;
reg_rcsr = &(this->registers[slu_idx_rcsr]); // @ base addr
strcpy(reg_rcsr->name, "RCSR"); // Receiver Status Register
reg_rcsr->active_on_dati = true;
reg_rcsr->active_on_dato = true;
reg_rcsr->reset_value = 0 & ! RCSR_RCVR_DONE;
reg_rcsr->writable_bits = 0xff;
reg_rbuf = &(this->registers[slu_idx_rbuf]); // @ base addr
strcpy(reg_rbuf->name, "RBUF"); // Receiver Buffer Register
reg_rbuf->active_on_dati = true; // no controller state change
reg_rbuf->active_on_dato = true;
reg_rbuf->reset_value = 0;
reg_rbuf->writable_bits = 0xff;
reg_xcsr = &(this->registers[slu_idx_xcsr]); // @ base addr
strcpy(reg_xcsr->name, "XCSR"); // Transmitter Status Register
reg_xcsr->active_on_dati = true;
reg_xcsr->active_on_dato = true;
reg_xcsr->reset_value = XCSR_XMIT_RDY; // set
reg_xcsr->writable_bits = 0xff;
reg_xbuf = &(this->registers[slu_idx_xbuf]); // @ base addr
strcpy(reg_xbuf->name, "XBUF"); //Transmitter Buffer Register
reg_xbuf->active_on_dati = true; // no controller state change
reg_xbuf->active_on_dato = true;
reg_xbuf->reset_value = 0;
reg_xbuf->writable_bits = 0xff;
// initialize serial format
baudrate.value = 9600;
mode.value = "8N1";
}
slu_c::~slu_c() {
}
bool slu_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
cport_nr = 2; /* UART2 */
if (enabled.new_value) {
// enable SLU: setup COM port
if (RS232_OpenComport(cport_nr, baudrate.value, mode.value.c_str())) {
ERROR("Can not open comport");
return false; // reject "enable"
}
// lock serial format settings
baudrate.readonly = true;
mode.readonly = true;
RS232_cputs(cport_nr, "Comport opened\n\r");
} else {
// disable SLU
RS232_CloseComport(cport_nr);
// lock serial format settings
baudrate.readonly = false;
mode.readonly = false;
}
}
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
//--------------------------------------------
// background worker.
void slu_c::worker(void) {
timeout_c timeout;
char mychar;
int i, n;
while (!worker_terminate) {
timeout.wait_ms(SLU_MSRATE_MS);
/* read character from socket */
if (!(RCSR_RCVR_DONE & rcsr.value)) {
bzero(buffer, BUFLEN);
#if 1
if (slu_maint.value) {
n = 1;
buffer[0] = rbuf.value;
buffer[1] = 0;
} else
#endif
{
/* read serial data, if any */
n = RS232_PollComport(cport_nr, (unsigned char*) buffer, 1);
}
for (i = 0; i < n; i++) {
mychar = buffer[i];
// transmit chr to bus
rbuf.value = mychar;
set_register_dati_value(reg_rbuf, rbuf.value, __func__);
// signal data on bus ready to read
rcsr.value |= RCSR_RCVR_DONE;
set_register_dati_value(reg_rcsr, rcsr.value, __func__);
}
}
// transfer received character to socket
if (!(XCSR_XMIT_RDY & xcsr.value)) {
buffer[0] = get_register_dato_value(reg_xbuf);
RS232_SendByte(cport_nr, buffer[0]);
// signal data written
xcsr.value |= XCSR_XMIT_RDY;
set_register_dati_value(reg_xcsr, xcsr.value, __func__);
}
}
}
// process DATI/DATO access to one of my "active" registers
// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS.
// The time between PRU event and program flow into this callback
// is determined by ARM Linux context switch
//
// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc:
// do not read back dati_flipflops.
void slu_c::on_after_register_access(unibusdevice_register_t *device_reg,
uint8_t unibus_control) {
if (unibus_control == UNIBUS_CONTROL_DATO) // bus write
set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__);
switch (device_reg->index) {
case slu_idx_rcsr:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
rcvr_interrupt_enable.value = !!(reg_rcsr->active_dato_flipflops
& (RCSR_RCVR_INT_ENB));
rdr_enable.value = !!(reg_rcsr->active_dato_flipflops & (RCSR_RDR_ENB));
}
break;
case slu_idx_xbuf:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
// signal data has been written to bus
xcsr.value &= ~ XCSR_XMIT_RDY;
set_register_dati_value(reg_xcsr, xcsr.value, __func__);
// get value from bus write and put it into rx buffer
xbuf.value = reg_xbuf->active_dato_flipflops;
}
break;
case slu_idx_xcsr:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
xmit_interrupt_enable.value = !!(reg_xcsr->active_dato_flipflops
& (XCSR_XMIT_INT_ENB));
slu_maint.value = !!(reg_xcsr->active_dato_flipflops & (XCSR_MAINT));
}
break;
case slu_idx_rbuf:
if (unibus_control == UNIBUS_CONTROL_DATI) { // bus read
// signal data has been read from bus
rcsr.value &= ~ RCSR_RCVR_DONE;
set_register_dati_value(reg_rcsr, rcsr.value, __func__);
}
break;
default:
break;
}
}
void slu_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
}
// UNIBUS INIT: clear all registers
void slu_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("slu_c::on_init()");
}
}
//--------------------------------------------------------------------------------------------------
ltc_c::ltc_c() :
unibusdevice_c() // super class constructor
{
// static config
name.value = "LTC";
type_name.value = "ltc_c";
log_label = "ltc";
set_default_bus_params(LTC_ADDR, LTC_VECTOR, LTC_LEVEL); // base addr, intr-vector, intr level
// init parameters
// controller has some register
register_count = ltc_idx_count;
reg_lks = &(this->registers[ltc_idx_lks]); // @ base addr
strcpy(reg_lks->name, "LKS"); // Line Clock Status Register
reg_lks->active_on_dati = true; // no controller state change
reg_lks->active_on_dato = true;
reg_lks->reset_value = 0;
reg_lks->writable_bits = 0xff;
}
ltc_c::~ltc_c() {
}
bool ltc_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic here
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// background worker.
void ltc_c::worker(void) {
timeout_c timeout;
while (!worker_terminate) {
if (ltc_input.value) {
//should really wait for LTC input trailing edge here
timeout.wait_ms(10000);
} else
timeout.wait_ms(LTC_MSRATE_MS);
#if 0
printf("[%o",buslatches_getval[0]);
#endif
if (lke.value) {
lks.value |= LKS_INT_MON;
set_register_dati_value(reg_lks, lks.value, __func__);
}
}
}
// process DATI/DATO access to one of my "active" registers
void ltc_c::on_after_register_access(unibusdevice_register_t *device_reg,
uint8_t unibus_control) {
if (unibus_control == UNIBUS_CONTROL_DATO) // bus write
set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__);
switch (device_reg->index) {
case ltc_idx_lks:
if (unibus_control == UNIBUS_CONTROL_DATI) { // bus read
// signal data has been read from bus
lks.value &= ~ LKS_INT_MON;
set_register_dati_value(reg_lks, lks.value, __func__);
}
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
lks.value = reg_lks->active_dato_flipflops;
ltc_interrupt_enable.value = !!(reg_lks->active_dato_flipflops & (LKS_INT_ENB));
#if 0
if (reg_lks->active_dato_flipflops && 1)
{
interrupt();
//DEBUG("Interrupt!");
}
#endif
}
break;
default:
break;
}
}
void ltc_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
}
// UNIBUS INIT: clear all registers
void ltc_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("ltc_c::on_init()");
}
}