1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-04-07 05:56:25 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Josh Dersch
2019-08-12 17:28:19 -07:00
134 changed files with 10459 additions and 7397 deletions

View File

@@ -1,28 +1,28 @@
/* ddrmem.cpp - Control the shared DDR RAM - used for UNIBUS memory
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#include <stdio.h>
#include <stdlib.h>
@@ -42,10 +42,9 @@
ddrmem_c *ddrmem;
ddrmem_c::ddrmem_c() {
log_label = "DDRMEM" ;
log_label = "DDRMEM";
}
// check allocated memory and print info
void ddrmem_c::info() {
INFO("Shared DDR memory: %u bytes available, %u bytes needed.", len, sizeof(ddrmem_t));
@@ -130,7 +129,7 @@ void ddrmem_c::fill_pattern(void) {
void ddrmem_c::fill_pattern_pru(void) {
// ddrmem_base_physical and _len already set
assert((uint32_t )mailbox->ddrmem_base_physical == base_physical);
mailbox_execute(ARM2PRU_DDR_FILL_PATTERN, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_DDR_FILL_PATTERN);
}
// set corrected values for emulated memory range
@@ -183,6 +182,6 @@ void ddrmem_c::unibus_slave(uint32_t startaddr, uint32_t endaddr) {
s = inputline(buf, sizeof(buf), NULL);
} while (strlen(s) == 0);
// clearing arm2pru_req stops the emulation
mailbox_execute(ARM2PRU_NONE, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_NONE);
}

View File

@@ -1,27 +1,27 @@
/* device.cpp - abstract base class for devices
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
Abstract device, with or without UNIBUS registers.
maybe mass storage controller, storage drive or other UNIBUS device
@@ -32,7 +32,7 @@
- has a worker()
- has a logger
- has parameters
*/
*/
#define _DEVICE_CPP_
#include <string.h>
@@ -55,30 +55,28 @@ list<device_c *> device_c::mydevices;
// called on cancel and exit()
static void device_worker_pthread_cleanup_handler(void *context) {
device_c *device = (device_c *) context;
device_worker_c *worker_instance = (device_worker_c *) context;
device_c *device = worker_instance->device;
#define this device // make INFO work
device->worker_terminate = false;
device->worker_terminated = true; // ended on its own or on worker_terminate
INFO("Worker terminated for device %s.", device->name.value.c_str());
device->worker_terminate = false;
device->worker_terminated = true; // ended on its own or on worker_terminate
worker_instance->running = false;
INFO("%s::worker(%d) terminated.", device->name.value.c_str(), worker_instance->instance);
// printf("cleanup for device %s\n", device->name.value.c_str()) ;
#undef this
}
static void *device_worker_pthread_wrapper(void *context) {
device_c *device = (device_c *) context;
int oldstate ; // not used
device_worker_c *worker_instance = (device_worker_c *) context;
device_c *device = worker_instance->device;
int oldstate; // not used
#define this device // make INFO work
// call real worker
INFO("%s::worker() started", device->name.value.c_str());
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate) ;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldstate) ; //ASYNCH not allowed!
device->worker_terminate = false;
device->worker_terminated = false;
pthread_cleanup_push(device_worker_pthread_cleanup_handler, device) ;
device->worker();
pthread_cleanup_pop(1) ; // call cleanup_handler on regular exit
INFO("%s::worker(%u) started", device->name.value.c_str(), worker_instance->instance);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldstate); //ASYNCH not allowed!
worker_instance->running = true;
pthread_cleanup_push(device_worker_pthread_cleanup_handler, worker_instance);
device->worker(worker_instance->instance);
pthread_cleanup_pop(1); // call cleanup_handler on regular exit
// not reached on pthread_cancel()
#undef this
return NULL;
@@ -92,17 +90,21 @@ device_c::device_c() {
parent = NULL;
worker_terminate = false;
worker_terminated = true;
// init workers
workers_terminate = false;
set_workers_count(1); // default: 1 worker
// do not link params to this device over param constructor
// creation order of vector vs params?
name.parameterized = this;
type_name.parameterized = this;
enabled.parameterized = this;
verbosity.parameterized = this;
verbosity.value = *log_level_ptr; // global default value from logger->logsource
enabled.value = false; // must be activated by emulation logic/user interaction
param_add(&name);
param_add(&type_name);
param_add(&enabled);
param_add(&emulation_speed);
param_add(&verbosity);
emulation_speed.value = 1;
@@ -125,7 +127,40 @@ device_c::~device_c() {
mydevices.erase(p);
}
// default is 1 worker. Default: null function, terminates if not overwritten by child class
// can be set > 1 if device needs multiple worker instances
// only to be called in constructors
void device_c::set_workers_count(unsigned workers_count) {
workers.resize(workers_count);
for (unsigned instance = 0; instance < workers_count; instance++) {
device_worker_c *worker_instance = &workers[instance];
worker_instance->device = this;
worker_instance->instance = instance;
worker_instance->running = false;
}
}
bool device_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
if (enabled.new_value)
workers_start();
else
workers_stop();
}
// all devices forward their "on_param_changed" to parent classes,
// until a class rejects a value.
// device_c is the grand pratnes and produces "OK" for unknown or passive parameters
return true;
}
// search device in global list mydevices[]
device_c *device_c::find_by_name(char *name) {
list<device_c *>::iterator it;
for (it = device_c::mydevices.begin(); it != device_c::mydevices.end(); ++it)
if (!strcasecmp((*it)->name.value.c_str(), name))
return *it;
return NULL;
}
// set priority to max, keep policy, return current priority
// do not change worker_sched_priority
@@ -251,51 +286,59 @@ void device_c::worker_init_realtime_priority(enum worker_priority_e priority) {
/* worker_start - executes threads
*
* use of C++11 std::thread failed:
* thead.join() crashes with random system_errors
* thread.join() crashes with random system_errors
* So use classic "pthread" wrapper
* TODO: crash still there, was caused by cross compile -> back to C++11 threads!
*/
void device_c::worker_start(void) {
worker_terminate = false;
{
void device_c::workers_start(void) {
workers_terminate = false;
for (unsigned instance = 0; instance < workers.size(); instance++) {
device_worker_c *worker_instance = &workers[instance];
worker_instance->running = true;
// start pthread
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setstacksize(&attr, 1024*1024);
int status = pthread_create(&worker_pthread, &attr, &device_worker_pthread_wrapper,
(void *) this);
int status = pthread_create(&worker_instance->pthread, &attr,
&device_worker_pthread_wrapper, (void *) worker_instance);
if (status != 0) {
FATAL("Failed to create pthread with status = %d", status);
}
pthread_attr_destroy(&attr) ; // why?
pthread_attr_destroy(&attr); // why?
}
}
void device_c::worker_stop(void) {
void device_c::workers_stop(void) {
timeout_c timeout;
int status;
if (worker_terminated) {
DEBUG("%s.worker_stop(): already terminated.", name.name.c_str());
return;
}
INFO("Waiting for %s.worker() to stop ...", name.value.c_str());
worker_terminate = true;
// 100ms
timeout.wait_ms(100);
// worker_wrapper must do "worker_terminated = true;" on exit
if (!worker_terminated) {
// if thread is hanging in pthread_cond_wait(): send a cancellation request
status = pthread_cancel(worker_pthread);
if (status != 0)
FATAL("Failed to send cancellation request to worker_pthread with status = %d", status);
}
// !! If crosscompling: this causes a crash in the worker thread
// !! at pthread_cond_wait() or other cancellation points.
// !! No problem for compiles build on BBB itself.
status = pthread_join(worker_pthread, NULL);
if (status != 0) {
FATAL("Failed to join worker_pthread with status = %d", status);
workers_terminate = true; // global signal to all instances
timeout.wait_ms(100);
for (unsigned instance = 0; instance < workers.size(); instance++) {
device_worker_c *worker_instance = &workers[instance];
// if (!worker_instance->running) {
// DEBUG("%s.worker_stop(%u): already terminated.", name.name.c_str(), instance);
// return;
// }
if (worker_instance->running) {
INFO("%s.worker(%u) not cooperative: cancel it ...", name.value.c_str(), instance);
// if thread is hanging in pthread_cond_wait(): send a cancellation request
status = pthread_cancel(worker_instance->pthread);
if (status != 0)
FATAL("Failed to send cancellation request to worker_pthread with status = %d",
status);
}
// !! If crosscompling: this causes a crash in the worker thread
// !! at pthread_cond_wait() or other cancellation points.
// !! No problem for compiles build on BBB itself.
status = pthread_join(worker_instance->pthread, NULL);
if (status != 0) {
FATAL("Failed to join worker_pthread with status = %d", status);
}
}
}

View File

@@ -1,28 +1,28 @@
/* device.hpp - abstract base class for devices
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _DEVICE_HPP_
#define _DEVICE_HPP_
@@ -32,79 +32,102 @@
#include <mutex>
using namespace std;
#include "utils.hpp"
#include "parameter.hpp"
#include "logsource.hpp"
// instance of a worker thread for a device
class device_c;
class device_worker_c {
public:
// thread for this worker instance
device_c *device; // link to parent
unsigned instance; // id of this running instance
pthread_t pthread;bool running; // run state
};
// abstract unibus device
// maybe mass storage controller, storage drive or other device
// sets device register values depending on internal status,
// reacts on register read/write over UNIBUS by evaluation of PRU events.
class device_c: public logsource_c, public parameterized_c {
private:
void workers_start(void);
void workers_stop(void);
public:
// the class holds a list of pointers to instantiated devices
// also needed to have a list of threads
static list<device_c *> mydevices;
device_c *parent ; // example: storagedrive_c.parent is storage-controller
device_c *parent; // example: storagedrive_c.parent is storage-controller
// name of instance: "RL3"
parameter_string_c name = parameter_string_c(NULL, "name", "name", /*readonly*/
true, "Unique identifier of device");
true, "Unique identifier of device");
// name of type: "RL02". normally readonly.
parameter_string_c type_name = parameter_string_c(NULL, "type", "type", /*readonly*/
true, "Type");
// NULL: do not link params to this device automatically over param constructor
parameter_unsigned_c emulation_speed = parameter_unsigned_c(NULL,
"emulation_speed", "es", false, "", "%d",
"1 = original speed, > 1: mechanics is this factor faster", 8, 10);
// "enabled": controls device installation to PRU and worker() state.
parameter_bool_c enabled = parameter_bool_c(NULL, "enabled", "en", true,
"device installed and ready to use?");
parameter_unsigned_c emulation_speed = parameter_unsigned_c(NULL, "emulation_speed", "es",
false, "", "%d", "1 = original speed, > 1: mechanics is this factor faster", 8, 10);
// 1 = original speed, > 1: mechanics is this factor faster
parameter_unsigned_c verbosity = parameter_unsigned_c(NULL,
"verbosity", "v", false, "", "%d",
"1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug", 8, 10);
parameter_unsigned_c verbosity = parameter_unsigned_c(NULL, "verbosity", "v", false, "",
"%d", "1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug", 8, 10);
// make data exchange with worker atomic
std::mutex worker_mutex ;
// std::mutex worker_mutex;
// scheduler settings for worker thread
int worker_sched_policy;
int worker_sched_priority;
enum worker_priority_e {
none_rt, // under all RT priorities
rt_device, // all controeller and storage worker
none_rt, // lower than all RT priorities
rt_device, // all controller and storage worker
rt_max // 100% CPU, uninterruptable
} ;
void worker_init_realtime_priority(enum worker_priority_e priority) ;
void worker_boost_realtime_priority(void) ;
void worker_restore_realtime_priority(void) ;
};
void worker_init_realtime_priority(enum worker_priority_e priority);
void worker_boost_realtime_priority(void);
void worker_restore_realtime_priority(void);
device_c();
device_c(void);
virtual ~device_c(); // class with virtual functions should have virtual destructors
void set_workers_count(unsigned workers_count);
virtual bool on_param_changed(parameter_c *param);
// search in mydevices
static device_c *find_by_name(char *name);
// a device can be powered down. use this to define power-up values
volatile bool power_down ;
volatile bool power_down;
virtual void on_power_changed(void) = 0; // reset device, UNIBUS DC_LO
// every device has a INIT signal, which can be active (asserted) or inactive
// set/release device from INIT state
volatile bool init_asserted ;
volatile bool init_asserted;
virtual void on_init_changed(void) = 0; // reset device, like UNIBUS INIT
// worker thread
volatile bool worker_terminate; // cmd flag to worker()
volatile bool worker_terminated; // ACK flag from worker()
// worker threads: multiple instances of single worker() are running in parallel
// device must implement a worker(instance) {
// switch((instance) { ... } }
// 'instance' from 0 .. worker_count-1
volatile bool workers_terminate; // cmd flag to all worker() instances
vector<device_worker_c> workers;
pthread_t worker_pthread;
void worker_start(void);
void worker_stop(void);
virtual void worker(void) = 0; // background worker function
// default background worker function for devices without need.
virtual void worker(unsigned instance) {
UNUSED(instance);
printf("Warning: default device_c::worker() called, better use set_worker_count(0) ");
}
};
#endif

View File

@@ -616,7 +616,7 @@ void buslatches_register() {
// PRU1 does it
void buslatches_pru_reset() {
assert(pru->prucode_id == pru_c::PRUCODE_TEST);
mailbox_execute(ARM2PRU_BUSLATCH_INIT, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_BUSLATCH_INIT);
}
// read the REG_DATIN[0..7] pins
@@ -627,7 +627,7 @@ unsigned buslatches_getval(unsigned reg_sel) {
while (mailbox->buslatch.addr != reg_sel)
; // cache !
mailbox_execute(ARM2PRU_BUSLATCH_GET, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_BUSLATCH_GET);
return mailbox->buslatch.val; // PRU1 has put the result here
}
@@ -639,7 +639,7 @@ void buslatches_setval(unsigned reg_sel, unsigned bitmask, unsigned val) {
mailbox->buslatch.bitmask = bitmask & 0xff;
mailbox->buslatch.val = val;
mailbox_execute(ARM2PRU_BUSLATCH_SET, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_BUSLATCH_SET);
}
// some pattern tests on a register latch
@@ -849,7 +849,7 @@ void buslatches_test_simple_pattern_multi(unsigned pattern) {
mailbox->buslatch_exerciser.pattern = (pass_no
% MAILBOX_BUSLATCH_EXERCISER_PATTERN_COUNT);
mailbox_execute(ARM2PRU_BUSLATCH_EXERCISER, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_BUSLATCH_EXERCISER);
// check: mailbox readvalues == write values ?
for (unsigned i = 0; i < BUSLATCHES_COUNT; i++) {

View File

@@ -83,11 +83,11 @@ typedef struct {
#define MAX_GPIOCOUNT 100
// test pins
// set 1 -> pin auf H
#define SET_DEBUG_PIN0(n) GPIO_SETVAL(gpios->led[0], !!(n))
#define SET_DEBUG_PIN1(n) GPIO_SETVAL(gpios->led[1], !!(n))
#define SET_DEBUG_PIN2(n) GPIO_SETVAL(gpios->led[2], !!(n))
#define SET_DEBUG_PIN3(n) GPIO_SETVAL(gpios->led[3], !!(n))
// SET(1) -> pin auf H, LED OFF
#define ARM_DEBUG_PIN0(n) GPIO_SETVAL(gpios->led[0], !!(n))
#define ARM_DEBUG_PIN1(n) GPIO_SETVAL(gpios->led[1], !!(n))
#define ARM_DEBUG_PIN2(n) GPIO_SETVAL(gpios->led[2], !!(n))
#define ARM_DEBUG_PIN3(n) GPIO_SETVAL(gpios->led[3], !!(n))
class gpios_c: public logsource_c {
private:
@@ -117,7 +117,6 @@ public:
void test_loopback(void);
};
#define BUSLATCHES_COUNT 8
// save current state uf gpios and registers,
@@ -235,7 +234,7 @@ void buslatches_setval(unsigned reg_sel, unsigned bitmask, unsigned val);
unsigned buslatches_getval(unsigned reg_sel);
void buslatches_test_simple_pattern(unsigned pattern, unsigned reg_sel);
void buslatches_test_simple_pattern_multi( unsigned pattern);
void buslatches_test_simple_pattern_multi(unsigned pattern);
void buslatches_test_timing(uint8_t addr_0_7, uint8_t addr_8_15, uint8_t data_0_7,
uint8_t data_8_15);

View File

@@ -1,28 +1,28 @@
/* iopageregister.cpp: handle ARM-PRU shared struct with device register descriptors
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#define _IOPAGEREGISTER_CPP_

View File

@@ -1,29 +1,28 @@
/* mailbox.cpp: datastructs common to ARM and PRU
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#define _MAILBOX_CPP_
@@ -39,8 +38,7 @@
// is located in PRU 12kb shared memory.
// address symbol "" fetched from linker map
volatile mailbox_t *mailbox;
volatile mailbox_t *mailbox = NULL;
// Init all fields, most to 0's
int mailbox_connect(void) {
@@ -57,7 +55,7 @@ int mailbox_connect(void) {
// now ARM and PRU can access the mailbox
memset((void*)mailbox, 0, sizeof(mailbox_t)) ;
memset((void*) mailbox, 0, sizeof(mailbox_t));
// tell PRU location of shared DDR RAM
mailbox->ddrmem_base_physical = (ddrmem_t *) ddrmem->base_physical;
@@ -65,11 +63,9 @@ int mailbox_connect(void) {
return 0;
}
void mailbox_print(void) {
printf("INFO: Content of mailbox to PRU:\n"
"arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp);
"arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp);
}
/* simulate simple register accesses:
@@ -103,21 +99,19 @@ void mailbox_test1() {
}
/* start cmd to PRU via mailbox. Wait until ready
* mailbox union members must have been filled
* mailbox union members must have been filled.
*/
uint32_t xxx ;
void mailbox_execute(uint8_t request, uint8_t stopcode) {
uint32_t xxx;
void mailbox_execute(uint8_t request) {
// write to arm2pru_req must be last memory operation
__sync_synchronize();
while (mailbox->arm2pru_req != ARM2PRU_NONE)
; // wait to complete
mailbox->arm2pru_req = request; // go!
do {
xxx = mailbox-> arm2pru_req ;
if (mailbox->events.eventmask) {
// event not processed? will hang DMA.
// printf("WARNING: Unprocessed mailbox.events.eventmask = 0x%x\n", (unsigned) mailbox->events.eventmask) ;
// mailbox->events.eventmask = 0 ;
}
} while (xxx != stopcode) ;
while (mailbox->arm2pru_req != stopcode)
xxx = mailbox->arm2pru_req;
} while (xxx != ARM2PRU_NONE);
while (mailbox->arm2pru_req != ARM2PRU_NONE)
; // wait until processed
}

View File

@@ -2,37 +2,37 @@
Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com
All rights reserved.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12-nov-2018 JH entered beta phase
18-Jun-2017 JH created
12-nov-2018 JH entered beta phase
18-Jun-2017 JH created
*/
#define _MEMORYIMAGE_CPP_
@@ -132,7 +132,7 @@ bool memoryimage_c::load_binary(const char *fname) {
unsigned wordidx, n;
fin = fopen(fname, "rb");
if (!fin) {
printf("%s\n",fileErrorText("Error opening file %s for read", fname));
printf("%s\n", fileErrorText("Error opening file %s for read", fname));
return false;
}
// try to read max address range, shorter files are OK
@@ -149,7 +149,7 @@ void memoryimage_c::save_binary(const char *fname, unsigned bytecount) {
unsigned n;
fout = fopen(fname, "wb");
if (!fout) {
printf("%s\n",fileErrorText("Error opening file %s for write", fname));
printf("%s\n", fileErrorText("Error opening file %s for write", fname));
return;
}
// try to read max address range, shorter files are OK
@@ -204,7 +204,7 @@ bool memoryimage_c::load_addr_value_text(const char *fname) {
fin = fopen(fname, "r");
if (!fin) {
printf("%s\n", fileErrorText("Error opening file %s for write", fname)) ;
printf("%s\n", fileErrorText("Error opening file %s for write", fname));
return false;
}
entry_address = MEMORY_ADDRESS_INVALID; // not known
@@ -467,7 +467,7 @@ bool memoryimage_c::load_macro11_listing(const char *fname, const char *entrylab
tokenidx = 0; // # of number processed
ready = false;
addr = 0;
line_addr = 0 ;
line_addr = 0;
while (!ready) {
while (*tp && isspace(*tp))
tp++; // skip white space
@@ -538,7 +538,7 @@ bool memoryimage_c::load_papertape(const char *fname) {
entry_address = MEMORY_ADDRESS_INVALID; // not yet known
stream_byte_index = 0;
block_byte_size = addr = 0 ; // -Wmaybe-uninitialized
block_byte_size = addr = 0; // -Wmaybe-uninitialized
while (!feof(fin)) {
b = fgetc(fin);
// ERROR("[0x%04x] state=%d b=0x%02x sum=0x%02x block_byte_idx=%d",

View File

@@ -2,37 +2,37 @@
Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com
All rights reserved.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12-nov-2018 JH entered beta phase
18-Jun-2017 JH created
12-nov-2018 JH entered beta phase
18-Jun-2017 JH created
*/
@@ -83,7 +83,7 @@ public:
int entry_address; // start address, if found. MEMORY_ADDRESS_INVALID = invalid
memoryimage_c() {
log_label = "MEMIMG" ;
log_label = "MEMIMG";
}
void init(void);
@@ -94,12 +94,11 @@ public:
assert_address(addr);
return valid[wordidx];
}
void fill(uint16_t fillword) ;
void fill(uint16_t fillword);
void get_addr_range(unsigned *first, unsigned* last);
unsigned get_word_count(void);
void set_addr_range(unsigned first, unsigned last) ;
void set_addr_range(unsigned first, unsigned last);
bool load_addr_value_text(const char *fname);bool load_macro11_listing(const char *fname,
const char *entrylabel);bool load_papertape(const char *fname);bool load_binary(

View File

@@ -32,9 +32,8 @@
#include "device.hpp"
#define _PARAMETER_CPP_
parameter_c::parameter_c(parameterized_c *parameterized, string name, string shortname,
bool readonly, string unit, string format, string info) {
bool readonly, string unit, string format, string info) {
this->parameterized = parameterized;
this->name = name;
this->shortname = shortname;
@@ -53,8 +52,7 @@ parameter_c::~parameter_c() {
// to be implemented in subclass
void parameter_c::parse(string text) {
throw bad_parameter_parse(
"parameter_c::parse(" + text + ") to be implemented in subclass");
throw bad_parameter_parse("parameter_c::parse(" + text + ") to be implemented in subclass");
}
// convert to text
@@ -73,16 +71,21 @@ parameter_string_c::parameter_string_c(parameterized_c *parameterized, string na
parameter_string_c::~parameter_string_c() {
}
void parameter_string_c::set(string new_value) {
if (value == new_value)
return; // call "on_change" only on change
this->new_value = new_value;
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
}
// string parsing is just copying
void parameter_string_c::parse(string text) {
if (readonly)
throw bad_parameter_readonly("Parameter \"" + name + "\" is read-only");
new_value = text;
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
set(text);
}
string *parameter_string_c::render() {
@@ -90,13 +93,24 @@ string *parameter_string_c::render() {
return &printbuffer;
}
parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name, string shortname,
parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name,
string shortname,
bool readonly, string info) :
parameter_c(parameterized, name, shortname, readonly, "", "", info) {
value = false;
}
// bool accepty 0/1, y*/n*, t*/f*
void parameter_bool_c::set(bool new_value) {
if (value == new_value)
return; // call "on_change" only on change
// reject parsed value, if device parameter check complains
this->new_value = new_value;
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
}
// bool accepts 0/1, y*/n*, t*/f*
void parameter_bool_c::parse(string text) {
char c;
if (readonly)
@@ -112,10 +126,7 @@ void parameter_bool_c::parse(string text) {
new_value = false;
else
throw bad_parameter_parse("Illegal boolean expression \"" + text + "\"");
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
set(new_value);
}
string *parameter_bool_c::render() {
@@ -134,6 +145,17 @@ parameter_unsigned_c::parameter_unsigned_c(parameterized_c *parameterized, strin
this->base = base;
value = 0;
}
void parameter_unsigned_c::set(unsigned new_value) {
if (value == new_value)
return; // call "on_change" only on change
this->new_value = new_value;
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
}
void parameter_unsigned_c::parse(string text) {
char *endptr;
if (readonly)
@@ -141,15 +163,11 @@ void parameter_unsigned_c::parse(string text) {
TRIM_STRING(text);
new_value = strtol(text.c_str(), &endptr, base);
if (*endptr)
throw bad_parameter_parse(
"Format error in \"" + text + "\" at \"" + *endptr + "\"");
throw bad_parameter_parse("Format error in \"" + text + "\" at \"" + *endptr + "\"");
if (new_value & ~BitmaskFromLen32[bitwidth]) //
throw bad_parameter_parse(
"Number " + to_string(new_value) + " exceeds bitwidth " + to_string(bitwidth));
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
set(new_value);
}
string *parameter_unsigned_c::render() {
@@ -168,6 +186,16 @@ parameter_unsigned64_c::parameter_unsigned64_c(parameterized_c *parameterized, s
value = 0;
}
void parameter_unsigned64_c::set(uint64_t new_value) {
if (value == new_value)
return; // call "on_change" only on change
this->new_value = new_value;
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
}
void parameter_unsigned64_c::parse(string text) {
char *endptr;
if (readonly)
@@ -175,15 +203,11 @@ void parameter_unsigned64_c::parse(string text) {
TRIM_STRING(text);
new_value = strtoll(text.c_str(), &endptr, base);
if (*endptr)
throw bad_parameter_parse(
"Format error in \"" + text + "\" at \"" + *endptr + "\"");
throw bad_parameter_parse("Format error in \"" + text + "\" at \"" + *endptr + "\"");
if (new_value & ~BitmaskFromLen64[bitwidth]) //
throw bad_parameter_parse(
"Number " + to_string(new_value) + " exceeds bitwidth " + to_string(bitwidth));
// reject parsed value, if device parameter check complains
if (parameterized == NULL || parameterized->on_param_changed(this))
value = new_value;
set(new_value);
}
string *parameter_unsigned64_c::render() {
@@ -193,7 +217,6 @@ string *parameter_unsigned64_c::render() {
return &printbuffer;
}
// add reference to parameter. It will be automatically deleted
parameter_c *parameterized_c::param_add(parameter_c *param) {
parameter.push_back(param);
@@ -216,4 +239,3 @@ parameter_c *parameterized_c::param_by_name(string name) {
return NULL;
}

View File

@@ -1,28 +1,28 @@
/* parameter.hpp: collection of typed name/value pairs, used by devices and other objects
Copyright (c) 2018-2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018-2019, 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:
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 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.
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.
16-mar-2019 JH unlinked from devices
12-nov-2018 JH entered beta phase
*/
16-mar-2019 JH unlinked from devices
12-nov-2018 JH entered beta phase
*/
#ifndef _PARAMETER_HPP_
#define _PARAMETER_HPP_
@@ -33,43 +33,45 @@
#include <exception>
using namespace std;
class bad_parameter: public exception {
private:
string message;
public:
bad_parameter(string message) {
(this->message = message);
}
const char* what() const noexcept {
return message.c_str();
}
} ;
private:
string message;
public:
bad_parameter(string message) {
(this->message = message);
}
const char* what() const noexcept {
return message.c_str();
}
};
class bad_parameter_parse: public bad_parameter {
public:
bad_parameter_parse(string message): bad_parameter(message) { }
bad_parameter_parse(string message) :
bad_parameter(message) {
}
};
class bad_parameter_check: public bad_parameter {
public:
bad_parameter_check(string message): bad_parameter(message) { }
bad_parameter_check(string message) :
bad_parameter(message) {
}
};
class bad_parameter_readonly: public bad_parameter {
public:
bad_parameter_readonly(string message): bad_parameter(message) { }
bad_parameter_readonly(string message) :
bad_parameter(message) {
}
};
class parameterized_c ;
class parameterized_c;
class parameter_c {
private:
public:
parameterized_c *parameterized ; // link to parent object
parameterized_c *parameterized; // link to parent object
string name;
string shortname;
bool readonly;
@@ -78,7 +80,8 @@ public:
string format; // printf, scanf
// parameter_c();
parameter_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string unit, string format, string info);
parameter_c(parameterized_c *parameterized, string name, string shortname, bool readonly,
string unit, string format, string info);
virtual ~parameter_c(); // class with virtual functions should have virtual destructors
// convert text to value. result: ok?
@@ -88,30 +91,32 @@ public:
virtual string *render(void);
};
class parameter_string_c: public parameter_c {
public:
// dynamic state
string value;
string new_value ; // after parse, checked by device.on_param_change_check()
string new_value;
parameter_string_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string info);
parameter_string_c(parameterized_c *parameterized, string name, string shortname,
bool readonly, string info);
~parameter_string_c();
string *render(void) override;
void parse(string text) override;
void set(string new_value);
};
class parameter_bool_c: public parameter_c {
public:
// dynamic state
bool value;
bool new_value ; // after parse, checked by device.on_param_change_check()
bool new_value;
parameter_bool_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string info);
parameter_bool_c(parameterized_c *parameterized, string name, string shortname,
bool readonly, string info);
parameter_bool_c();
string *render(void) override;
void parse(string text) override;
void set(bool new_value);
};
class parameter_unsigned_c: public parameter_c {
@@ -123,12 +128,14 @@ public:
// dynamic state
unsigned value;
unsigned new_value ; // after parse, checked by device.on_param_change_check()
unsigned new_value;
parameter_unsigned_c(parameterized_c *parameterized, string name,string shortname, bool readonly, string unit, string format,
string info, unsigned bitwidth, unsigned base);
parameter_unsigned_c(parameterized_c *parameterized, string name, string shortname,
bool readonly, string unit, string format, string info, unsigned bitwidth,
unsigned base);
string *render(void) override;
void parse(string text) override;
void set(unsigned new_value);
};
class parameter_unsigned64_c: public parameter_c {
@@ -140,20 +147,21 @@ public:
// dynamic state
uint64_t value;
uint64_t new_value ; // after parse, checked by device.on_param_change_check()
uint64_t new_value;
parameter_unsigned64_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string unit, string format,
string info, unsigned bitwidth, unsigned base);
parameter_unsigned64_c(parameterized_c *parameterized, string name, string shortname,
bool readonly, string unit, string format, string info, unsigned bitwidth,
unsigned base);
string *render(void) override;
void parse(string text) override;
void set(uint64_t new_value);
};
// objects with parameters are "parameterized" and inherit this.
class parameterized_c {
public:
vector<parameter_c *> parameter;
public:
vector<parameter_c *> parameter;
// register parameter
parameter_c *param_add(parameter_c *param);
@@ -161,11 +169,10 @@ class parameterized_c {
parameter_c *param_by_name(string name);
// sort?
// called after param value changed.
// result: false = "new_value" not excepted, error printed.
virtual bool on_param_changed(parameter_c *param) = 0 ;
} ;
virtual bool on_param_changed(parameter_c *param) = 0;
};
#endif

View File

@@ -0,0 +1,107 @@
/* priorityrequest.cpp: DMA or iNTR request of an device
Copyright (c) 2019 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.
jul-2019 JH start: multiple parallel arbitration levels
*/
#include <assert.h>
#include <algorithm>
#include "unibusdevice.hpp"
#include "priorityrequest.hpp"
priority_request_c::priority_request_c(unibusdevice_c *device) {
this->log_label = "REQ";
this->device = device;
this->complete = false;
this->executing_on_PRU = false;
this->slot = 0xff; // uninitialized, asserts() if used
complete_mutex = PTHREAD_MUTEX_INITIALIZER;
//complete_cond = PTHREAD_COND_INITIALIZER; // PRU signal notifies request on completeness
}
priority_request_c::~priority_request_c() {
// not used, but need some virtual func for dynamic_cast()
}
void priority_request_c::set_priority_slot(uint8_t priority_slot) {
assert(priority_slot > 0); // 0 reserved
assert(priority_slot < PRIORITY_SLOT_COUNT);
unibusdevice_c *ubdevice = unibusdevice_c::find_by_request_slot(priority_slot);
if (ubdevice && ubdevice != this->device) {
WARNING("Slot %u already used by device %s", (unsigned) priority_slot, ubdevice->name.value.c_str());
}
this->slot = priority_slot;
// todo: check for collision with all other devices, all other requests
}
// create invalid requests, is setup by unibusadapter
dma_request_c::dma_request_c(unibusdevice_c *device) :
priority_request_c(device) {
this->level_index = PRIORITY_LEVEL_INDEX_NPR;
this->success = false;
// register request for device
if (device) {
device->dma_requests.push_back(this);
}
}
dma_request_c::~dma_request_c() {
if (device) {
// find and erase from device's request list
std::vector<dma_request_c *>::iterator it = std::find(device->dma_requests.begin(),
device->dma_requests.end(), this);
device->dma_requests.erase(it);
}
}
// create invalid requests, is setup by unibusadapter
intr_request_c::intr_request_c(unibusdevice_c *device) :
priority_request_c(device) {
// convert UNIBUS level 4,5,6,7 to internal priority, see REQUEST_INDEX_*
this->level_index = 0xff; // uninitialized, asserts() if used
this->vector = 0xffff; // uninitialized, asserts() if used
this->signal_level = 0;
if (device)
device->intr_requests.push_back(this);
}
intr_request_c::~intr_request_c() {
if (device) {
// find and erase from device's request list
std::vector<intr_request_c *>::iterator it = std::find(device->intr_requests.begin(),
device->intr_requests.end(), this);
device->intr_requests.erase(it);
}
}
void intr_request_c::set_level(uint8_t level) {
assert(level >= 4 && level <= 7);
this->level_index = level - 4; // one of PRIORITY_LEVEL_INDEX_*
}
void intr_request_c::set_vector(uint16_t vector) {
assert((vector & 3) == 0); // multiple of 2 words
this->vector = vector;
}

View File

@@ -0,0 +1,183 @@
/* priorityrequest.hpp: DMA or iNTR request of an device
Copyright (c) 2019 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.
jul-2019 JH start: multiple parallel arbitration levels
*/
/*
Handling priorities of Arbitration Requests:
1. Priority of arbitration levels
Ascending priority: INTR BR4,5,6,7, DMA NPR
=> 5 Priority Arbitration Levels encoded with index 0..4
2. Priority within one request level
Priority for Requests of same level given by backplane slot.
Backplane closests to CPU is granted first => highest priority
So priority of a request is given by two coordinates: level and slot.
Implementation:
all 5 levels are handled in parallel: priority_request_level_c [5])
In each level, a refernece array[slot] holds open requests.
For fast determination of lowest active slot, a bitarray
marks active slots.
*/
#ifndef _PRIORITYREQUEST_HPP_
#define _PRIORITYREQUEST_HPP_
#include <stdint.h>
#include <pthread.h>
#include "logsource.hpp"
// linear indexes for different UNIBUS arbitration levels
#define PRIORITY_LEVEL_INDEX_BR4 0
#define PRIORITY_LEVEL_INDEX_BR5 1
#define PRIORITY_LEVEL_INDEX_BR6 2
#define PRIORITY_LEVEL_INDEX_BR7 3
#define PRIORITY_LEVEL_INDEX_NPR 4
#define PRIORITY_LEVEL_COUNT 5
#define PRIORITY_SLOT_COUNT 32 // backplane slot numbers 0..31 may be used
class unibusdevice_c;
// (almost) abstract base class for dma and intr requests
class priority_request_c: public logsource_c {
friend class intr_request_c;
friend class dma_request_c;
friend class unibusadapter_c;
private:
unibusdevice_c *device; // this device owns the request
// maybe NULL, in request is not used for device meualtion
// (test console EXAM, DEPOSIT for example).
// internal priority index of a request level, see REQUEST_INDEX_*
uint8_t level_index; // is BR4567,NPR level - 4
uint8_t slot; // backplane slot which triggered request
public:
// better make state variables volatile, accessed by unibusadapter::worker
volatile bool executing_on_PRU; // true between schedule to PRU and compelte signal
volatile bool complete;
// PRU -> signal -> worker() -> request -> device. INTR/DMA
pthread_mutex_t complete_mutex;
//pthread_cond_t complete_cond; // PRU signal notifies request on completeness
priority_request_c(unibusdevice_c *device);
virtual ~priority_request_c(); // not used, but need dynamic_cast
void set_priority_slot(uint8_t slot);
uint8_t get_priority_slot(void) {
return slot;
}
};
class dma_request_c: public priority_request_c {
friend class unibusadapter_c;
public:
dma_request_c(unibusdevice_c *device);
~dma_request_c();
// const for all chunks
uint8_t unibus_control; // DATI,DATO
uint32_t unibus_start_addr;
uint32_t unibus_end_addr;
uint16_t* buffer;
uint32_t wordcount;
// DMA transaction are divided in to smaller DAT transfer "chunks"
uint32_t chunk_max_words; // max is PRU capacity PRU_MAX_DMA_WORDCOUNT (512)
uint32_t chunk_unibus_start_addr; // current chunk
uint32_t chunk_words; // size of current chunks
volatile bool success; // DMA can fail with bus timeout
// return ptr to chunk pos in buffer
uint16_t *chunk_buffer_start(void) {
return buffer + (chunk_unibus_start_addr - unibus_start_addr) / 2;
}
// words already transfered in previous chunks
uint32_t wordcount_completed_chunks(void) {
return (chunk_unibus_start_addr - unibus_start_addr) / 2;
}
};
struct unibusdevice_register_struct;
// forward
class intr_request_c: public priority_request_c {
friend class unibusadapter_c;
public:
enum interrupt_edge_enum {
INTERRUPT_EDGE_NONE, INTERRUPT_EDGE_RAISING, INTERRUPT_EDGE_FALLING
};
private:
uint16_t vector; // PDP-11 interrupt vector
// optionally register, with which a device signals presence of interrupt condition
struct unibusdevice_register_struct *interrupt_register;
uint16_t interrupt_register_value;
// static level of some device INTR signal.
// raising edge calculated with edge_detect()
bool signal_level;
public:
intr_request_c(unibusdevice_c *device);
~intr_request_c();
void set_level(uint8_t level);
void set_vector(uint16_t vector);
uint8_t get_level(void) {
return level_index + 4;
}
uint8_t get_vector(void) {
return vector;
}
// service for device logic: calculate change of static INTR condition
void edge_detect_reset() {
signal_level = 0;
}
// detect raising edge of interrupt level
enum interrupt_edge_enum edge_detect(bool new_signal_level) {
if (signal_level == new_signal_level)
return INTERRUPT_EDGE_NONE;
else {
// change: which edge?
signal_level = new_signal_level;
if (signal_level)
return INTERRUPT_EDGE_RAISING;
else
return INTERRUPT_EDGE_FALLING;
}
}
};
#endif

View File

@@ -134,7 +134,6 @@ int pru_c::start(enum prucode_enum prucode_id) {
// use stop() before restart()
assert(this->prucode_id == PRUCODE_NONE);
/* initialize PRU */
if ((rtn = prussdrv_init()) != 0) {
ERROR("prussdrv_init() failed");
@@ -153,7 +152,6 @@ int pru_c::start(enum prucode_enum prucode_id) {
goto error;
}
/*
http://credentiality2.blogspot.com/2015/09/beaglebone-pru-ddr-memory-access.html
* get pointer to shared DDR
@@ -208,7 +206,7 @@ int pru_c::start(enum prucode_enum prucode_id) {
// verify PRU1 is executing its command loop
mailbox->arm2pru_req = ARM2PRU_NOP;
timeout.wait_ms(1);
if (mailbox->arm2pru_req != ARM2PRU_NONE) {
if (mailbox->arm2pru_req != ARM2PRU_NONE) {
FATAL("PRU1 is not executing its command loop");
goto error;
}

View File

@@ -2,37 +2,37 @@
Copyright (c) 2018, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com
All rights reserved.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18-apr-2019 JH added PRU code dictionary
12-nov-2018 JH entered beta phase
18-apr-2019 JH added PRU code dictionary
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU_HPP_
@@ -43,8 +43,6 @@
#include "logsource.hpp"
/*** PRU Shared addresses ***/
// Mailbox page & offset in PRU internal shared 12 KB RAM
// Accessible by both PRUs, must be located in shared RAM
@@ -57,35 +55,31 @@
#define PRU0_ENTRY_ADDR 0x00000000
#define PRU1_ENTRY_ADDR 0x00000000
#ifndef PRU_MAILBOX_RAM_ID
#define PRU_MAILBOX_RAM_ID PRUSS0_SHARED_DATARAM
#define PRU_MAILBOX_RAM_OFFSET 0
#define PRU_MAILBOX_RAM_ID PRUSS0_SHARED_DATARAM
#define PRU_MAILBOX_RAM_OFFSET 0
#endif
// Device register page & offset in PRU0 8KB RAM mapped into PRU1 space
// offset 0 == addr 0x2000 in linker cmd files for PRU1 projects.
// For use with prussdrv_map_prumem()
#ifndef PRU_DEVICEREGISTER_RAM_ID
#define PRU_DEVICEREGISTER_RAM_ID PRUSS0_PRU0_DATARAM
#define PRU_DEVICEREGISTER_RAM_OFFSET 0
#define PRU_DEVICEREGISTER_RAM_ID PRUSS0_PRU0_DATARAM
#define PRU_DEVICEREGISTER_RAM_OFFSET 0
#endif
class pru_c: public logsource_c {
public:
// IDs for code variants, so callers can select one
enum prucode_enum {
PRUCODE_EOD = 0, // special marker: end of dictionary
PRUCODE_NONE = 0, // no code running, RPU reset
PRUCODE_TEST = 1, // only selftest functions
PRUCODE_UNIBUS = 2 // regular UNIBUS operation
PRUCODE_EOD = 0, // special marker: end of dictionary
PRUCODE_NONE = 0, // no code running, RPU reset
PRUCODE_TEST = 1, // only selftest functions
PRUCODE_UNIBUS = 2 // regular UNIBUS operation
// with or without physical CPU for arbitration
} ;
};
public:
enum prucode_enum prucode_id ; // currently running code
enum prucode_enum prucode_id; // currently running code
pru_c();
int start(enum prucode_enum prucode_id);

View File

@@ -1,30 +1,30 @@
/* storagecontroller.cpp: a unibus device with several "storagedrives" attached
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
A unibus device with several "storagedrives"
supports the "attach" command
A unibus device with several "storagedrives"
supports the "attach" command
*/
#include "utils.hpp"
@@ -41,8 +41,15 @@ storagecontroller_c::~storagecontroller_c() {
// implements params, so must handle "change"
bool storagecontroller_c::on_param_changed(parameter_c *param) {
UNUSED(param) ;
return true ;
if (param == &enabled) {
if (!enabled.new_value)
// power/up/down attached drives, then plug to UNIBUS
// if disable, disable also the drives ("contreolelr plugged from UNIBUS)")
// on enable, leave them disabled (user may decide which to use)
for (unsigned i = 0; i < drivecount; i++)
storagedrives[i]->enabled.set(false);
}
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// forward BUS events to connected storage drives
@@ -65,21 +72,3 @@ void storagecontroller_c::on_init_changed() {
}
}
// start/stop threads of this and all drives
void storagecontroller_c::worker_start() {
vector<storagedrive_c*>::iterator it;
for (it = storagedrives.begin(); it != storagedrives.end(); it++) {
(*it)->worker_start();
}
device_c::worker_start();
}
void storagecontroller_c::worker_stop() {
device_c::worker_stop();
vector<storagedrive_c*>::iterator it;
for (it = storagedrives.begin(); it != storagedrives.end(); it++) {
(*it)->worker_stop();
}
}

View File

@@ -1,27 +1,27 @@
/* storagecontroller.hpp: a unibus device with several "storagedrives" attached
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
*/
@@ -41,15 +41,12 @@ public:
// does not instantiate the drives
storagecontroller_c(void);
virtual ~storagecontroller_c() ; // classes with virtual functions shoudl have virtual destructors
virtual ~storagecontroller_c(); // classes with virtual functions shoudl have virtual destructors
virtual bool on_param_changed(parameter_c *param) override;
virtual void on_power_changed() override ;
virtual void on_init_changed() override ;
virtual void on_drive_status_changed(storagedrive_c *drive) = 0 ;
void worker_start() ; // start/stop threads of this and all drives
void worker_stop() ;
virtual void on_power_changed() override;
virtual void on_init_changed() override;
virtual void on_drive_status_changed(storagedrive_c *drive) = 0;
};

View File

@@ -49,8 +49,8 @@ storagedrive_c::storagedrive_c(storagecontroller_c *controller) :
// implements params, so must handle "change"
bool storagedrive_c::on_param_changed(parameter_c *param) {
UNUSED(param);
return true;
// no own "enable" logic
return device_c::on_param_changed(param);
}
// http://www.cplusplus.com/doc/tutorial/files/
@@ -121,11 +121,11 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len
assert(!file_readonly); // caller must take care
// enlarge file in chunks until filled up to "position"
f.clear() ; // clear fail bit
f.clear(); // clear fail bit
f.seekp(0, ios::end); // move to current EOF
file_size = f.tellp(); // current file len
if (file_size < 0)
file_size = 0 ; // -1 on emtpy files
file_size = 0; // -1 on emtpy files
while (file_size < write_pos) {
// fill in '00' 'chunks up to desired end, but limit to max_chunk_size
int chunk_size = std::min(max_chunk_size, (int) (write_pos - file_size));
@@ -135,7 +135,7 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len
assert(fillbuff);
memset(fillbuff, 0, max_chunk_size);
}
f.clear() ; // clear fail bit
f.clear(); // clear fail bit
f.seekp(file_size, ios::beg); // move to end
f.write((const char *) fillbuff, chunk_size);
file_size += chunk_size;
@@ -148,7 +148,7 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len
assert(write_pos == 0);
else {
// move write pointer to target position
f.clear() ; // clear fail bit
f.clear(); // clear fail bit
f.seekp(write_pos, ios::beg);
p = f.tellp(); // position now < target?
assert(p == write_pos);

View File

@@ -102,8 +102,6 @@ public:
}
virtual void on_init_changed(void) {
}
virtual void worker(void) {
}
void test(void);
};

View File

@@ -20,7 +20,7 @@
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.
jul-2019 JH rewrite: multiple parallel arbitration levels
12-nov-2018 JH entered beta phase
*/
@@ -40,6 +40,8 @@
#include "memoryimage.hpp"
#include "mailbox.h" // for test of PRU code
#include "utils.hpp" // for test of PRU code
#include "unibusadapter.hpp" // DMA, INTR
#include "unibus.h"
/* Singleton */
@@ -47,8 +49,14 @@ unibus_c *unibus;
unibus_c::unibus_c() {
log_label = "UNIBUS";
dma_bandwidth_percent = 50;
dma_wordcount = MAX_DMA_WORDCOUNT;
dma_request = new dma_request_c(NULL);
// priority backplane slot # for helper DMA not important, as typically used stand-alone
// (no other devioces on the backplane active, except perhaps "testcontroller")
dma_request->set_priority_slot(16);
}
unibus_c::~unibus_c() {
delete dma_request;
}
/* return a 16 bit result, or TIMEOUT
@@ -96,40 +104,31 @@ void unibus_c::init(void) {
timeout.wait_ms(duration_ms);
buslatches_setval(7, BIT(3), 0);
*/
mailbox_execute(ARM2PRU_INITPULSE, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_INITPULSE);
}
/* Simulate a power cycle
*/
void unibus_c::powercycle(void) {
mailbox_execute(ARM2PRU_POWERCYCLE, ARM2PRU_NONE);
mailbox_execute(ARM2PRU_POWERCYCLE);
}
// do an UNIBUS INTR transaction with Arbitration by PDP-11 CPU
// todo: ARBITRATOR_MASTER?
void unibus_c::interrupt(uint8_t priority, uint16_t vector) {
assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS);
switch (priority) {
case 4:
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4;
void unibus_c::set_arbitration_mode(arbitration_mode_enum arbitration_mode) {
// switch pru to desired mode
switch (arbitration_mode) {
case unibus_c::ARBITRATION_MODE_NONE:
mailbox_execute(ARM2PRU_ARB_MODE_NONE);
break;
case 5:
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5;
case unibus_c::ARBITRATION_MODE_CLIENT:
mailbox_execute(ARM2PRU_ARB_MODE_CLIENT);
break;
case 6:
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6;
break;
case 7:
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7;
case unibus_c::ARBITRATION_MODE_MASTER:
mailbox_execute(ARM2PRU_ARB_MODE_MASTER);
break;
default:
ERROR("unibus_interrupt(): Illegal priority %u, aborting", priority);
return;
printf("Illegal arbitration_mode %d\n", (int) arbitration_mode);
exit(1);
}
mailbox->intr.vector = vector;
// mail last infinite, if processor priority above "priority"
// timeout ??
mailbox_execute(ARM2PRU_INTR, ARM2PRU_NONE);
}
// do a DMA transaction with or without abritration (arbitration_client)
@@ -137,36 +136,19 @@ void unibus_c::interrupt(uint8_t priority, uint16_t vector) {
// if result = timeout: =
// 0 = bus time, error address = mailbox->dma.cur_addr
// 1 = all transfered
bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_t control,
uint32_t startaddr, unsigned blocksize) {
// A limit for time used by DMA can be compiled-in
bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, bool blocking,
uint8_t control, uint32_t startaddr, uint16_t *buffer, unsigned wordcount) {
int dma_bandwidth_percent = 50; // use 50% of time for DMA, rest for running PDP-11 CPU
uint64_t dmatime_ns, totaltime_ns;
uint8_t dma_opcode = ARBITRATION_MODE_NONE; // inihibit compiler warnings
// can access bus with DMA when there's a Bus Arbitrator
assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS);
// TODO: assert pru->prucode_features & (PRUCODE_FEATURE_DMA) ???
// TODO: Arbitration Master waits for SACK, 11/34 blocks?
mailbox->dma.startaddr = startaddr;
mailbox->dma.control = control;
mailbox->dma.wordcount = blocksize;
timeout.start(0); // no timeout, just running timer
set_arbitration_mode(arbitration_mode); // changes PRU behaviour
switch (arbitration_mode) {
case unibus_c::ARBITRATION_MODE_NONE:
dma_opcode = ARM2PRU_DMA_ARB_NONE;
break;
case unibus_c::ARBITRATION_MODE_CLIENT:
dma_opcode = ARM2PRU_DMA_ARB_CLIENT;
break;
case unibus_c::ARBITRATION_MODE_MASTER:
dma_opcode = ARM2PRU_DMA_ARB_MASTER;
break;
default:
FATAL("Illegal arbitration_mode");
}
// wait until PRU ready
mailbox_execute(dma_opcode, ARM2PRU_NONE);
timeout.start_ns(0); // no timeout, just running timer
unibusadapter->DMA(*dma_request, blocking, control, startaddr, buffer, wordcount);
dmatime_ns = timeout.elapsed_ns();
// wait before next transaction, to reduce Unibus bandwidth
@@ -176,31 +158,26 @@ bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_
// 25% -> total = 4* dma
totaltime_ns = (dmatime_ns * 100) / dma_bandwidth_percent;
// whole transaction requires totaltime, dma already done
//INFO("DMA time= %lluus, waiting %lluus", dmatime_ns/1000, (totaltime_ns - dmatime_ns)/1000) ;
timeout.wait_ns(totaltime_ns - dmatime_ns);
return (mailbox->dma.cur_status == DMA_STATE_READY);
// all other end states are errors, only error is bus timeout
return dma_request->success; // only useful if blocking
}
/* scan unibus addresses ascending from 0.
* Stop on error, return first invalid address
* return 0: no memory found at all
* arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses
* words[]: buffer for whole UNIBUS address range, is filled with data
*/
uint32_t unibus_c::test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode) {
// tests chunks of 128 word
bool timeout;
unsigned addr = 0;
//SET_DEBUG_PIN0(0) ;
do {
// printf("unibus_test_sizer(): %06o..%06o\n", addr, addr+2*unibus_dma_wordcount-2) ;
timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, dma_wordcount);
addr += 2 * dma_wordcount; // prep for next round
} while (!timeout);
//SET_DEBUG_PIN0(1) ; // signal end
//SET_DEBUG_PIN0(0) ;
return mailbox->dma.cur_addr; // first non implemented address
set_arbitration_mode(arbitration_mode); // changes PRU behaviour
// one big transaction, automatically split in chunks
unibusadapter->DMA(*dma_request, true, UNIBUS_CONTROL_DATI, addr, testwords,
UNIBUS_WORDCOUNT);
return dma_request->unibus_end_addr; // first non implemented address
}
/*
@@ -213,32 +190,15 @@ uint32_t unibus_c::test_sizer(enum unibus_c::arbitration_mode_enum arbitration_m
//
// DMA blocksize can be choosen arbitrarily
void unibus_c::mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words,
unsigned start_addr, unsigned end_addr, unsigned block_wordcount,
bool *timeout) {
unsigned block_start_addr = 0;
unsigned n;
unsigned unibus_start_addr, unsigned unibus_end_addr, bool *timeout) {
unsigned wordcount = (unibus_end_addr - unibus_start_addr) / 2 + 1;
uint16_t *buffer_start_addr = words + unibus_start_addr / 2;
assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS);
assert(block_wordcount <= MAX_DMA_WORDCOUNT);
*timeout = 0;
for (block_start_addr = start_addr; !*timeout && block_start_addr <= end_addr;
block_start_addr += 2 * block_wordcount) {
// trunc last block
n = (end_addr - block_start_addr) / 2 + 1; // words left until memend
if (n < block_wordcount)
block_wordcount = n; //trunc last buffer
// fill data into dma buffer
memcpy((void*) (mailbox->dma.words), (void*) (words + block_start_addr / 2),
2 * block_wordcount);
/* for (n = 0; n < block_wordcount; n++) {
cur_addr = block_start_addr + 2 * n;
mailbox->dma.words[n] = words[cur_addr / 2];
}
*/*timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATO, block_start_addr,
block_wordcount);
if (*timeout) {
printf("\nWrite timeout @ 0%6o\n", mailbox->dma.cur_addr);
return;
}
*timeout = !dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, unibus_start_addr,
buffer_start_addr, wordcount);
if (*timeout) {
printf("\nWrite timeout @ 0%6o\n", mailbox->dma.cur_addr);
return;
}
}
@@ -247,34 +207,54 @@ void unibus_c::mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode,
// DMA blocksize can be choosen arbitrarily
// arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses
void unibus_c::mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words,
uint32_t start_addr, uint32_t end_addr, unsigned block_wordcount,
bool *timeout) {
unsigned block_start_addr = 0;
unsigned n;
uint32_t unibus_start_addr, uint32_t unibus_end_addr, bool *timeout) {
unsigned wordcount = (unibus_end_addr - unibus_start_addr) / 2 + 1;
uint16_t *buffer_start_addr = words + unibus_start_addr / 2;
assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS);
assert(block_wordcount <= MAX_DMA_WORDCOUNT);
*timeout = 0;
for (block_start_addr = start_addr; !*timeout && block_start_addr <= end_addr;
block_start_addr += 2 * block_wordcount) {
// trunc last block
n = (end_addr - block_start_addr) / 2 + 1; // words left until memend
if (n < block_wordcount)
block_wordcount = n; //trunc last buffer
*timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATI, block_start_addr,
block_wordcount);
*timeout = !dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, unibus_start_addr,
buffer_start_addr, wordcount);
if (*timeout) {
printf("\nRead timeout @ 0%6o\n", mailbox->dma.cur_addr);
return;
}
}
// read or write
void unibus_c::mem_access_random(enum unibus_c::arbitration_mode_enum arbitration_mode,
uint8_t unibus_control, uint16_t *words, uint32_t unibus_start_addr,
uint32_t unibus_end_addr, bool *timeout, uint32_t *block_counter) {
uint32_t block_unibus_start_addr, block_unibus_end_addr;
// in average, make 16 sub transactions
assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS);
assert(unibus_control == UNIBUS_CONTROL_DATI || unibus_control == UNIBUS_CONTROL_DATO);
block_unibus_start_addr = unibus_start_addr;
// split transaction in random sized blocks
uint32_t max_block_wordcount = (unibus_end_addr - unibus_start_addr + 2) / 2;
do {
uint16_t *block_buffer_start = words + block_unibus_start_addr / 2;
uint32_t block_wordcount;
do {
block_wordcount = random32_log(max_block_wordcount);
} while (block_wordcount < 1);
assert(block_wordcount < max_block_wordcount);
// wordcount limited by "words left to transfer"
block_wordcount = std::min(block_wordcount,
(unibus_end_addr - block_unibus_start_addr) / 2 + 1);
block_unibus_end_addr = block_unibus_start_addr + 2 * block_wordcount - 2;
assert(block_unibus_end_addr <= unibus_end_addr);
(*block_counter) += 1;
// printf("%06d: %5u words %06o-0%06o\n", *block_counter, block_wordcount, block_unibus_start_addr, block_unibus_end_addr) ;
*timeout = !dma(arbitration_mode, true, unibus_control, block_unibus_start_addr,
block_buffer_start, block_wordcount);
if (*timeout) {
printf("\nRead timeout @ 0%6o\n", mailbox->dma.cur_addr);
printf("\n%s timeout @ 0%6o\n", control2text(unibus_control),
mailbox->dma.cur_addr);
return;
}
// move from DMA buffer into words[]
memcpy((void*) (words + block_start_addr / 2), (void*) (mailbox->dma.words),
2 * block_wordcount);
/* for (n = 0; n < block_wordcount; n++) {
cur_addr = block_start_addr + 2 * n;
words[cur_addr / 2] = mailbox->dma.words[n];
}
*/}
block_unibus_start_addr = block_unibus_end_addr + 2;
} while (block_unibus_start_addr <= unibus_end_addr);
}
// arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses
@@ -285,7 +265,7 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode,
bool timeout = 0, mismatch = 0;
unsigned mismatch_count = 0;
uint32_t cur_test_addr;
unsigned block_wordcount;
unsigned pass_count = 0, total_read_block_count = 0, total_write_block_count = 0;
assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS);
@@ -298,25 +278,28 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode,
testwords[cur_test_addr / 2] = (cur_test_addr >> 1) & 0xffff;
/**** 2. Write memory ****/
progress.put("W"); //info : full memory write
block_wordcount = 113; // something queer
mem_write(arbitration_mode, testwords, start_addr, end_addr, block_wordcount, &timeout);
mem_write(arbitration_mode, testwords, start_addr, end_addr, &timeout);
/**** 3. read until ^C ****/
while (!SIGINTreceived && !timeout && !mismatch_count) {
pass_count++;
if (pass_count % 10 == 0)
progress.putf(" %d ", pass_count);
total_write_block_count++; // not randomized
total_read_block_count++;
progress.put("R");
block_wordcount = 67; // something queer
// read back into unibus_membuffer[]
mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr,
block_wordcount, &timeout);
mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, &timeout);
// compare
SET_DEBUG_PIN0(0) ;
for (mismatch_count = 0, cur_test_addr = start_addr; cur_test_addr <= end_addr;
cur_test_addr += 2) {
uint16_t cur_mem_val = membuffer->data.words[cur_test_addr / 2];
mismatch = (testwords[cur_test_addr / 2] != cur_mem_val);
if (mismatch) {
SET_DEBUG_PIN0(1) ; // trigger
SET_DEBUG_PIN0(0) ;
ARM_DEBUG_PIN0(1)
; // trigger
ARM_DEBUG_PIN0(0)
;
}
if (mismatch && ++mismatch_count <= MAX_ERROR_COUNT) // print only first errors
printf(
@@ -329,32 +312,39 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode,
case 2: // full write, full read
/**** 1. Full write generate test values */
// start_addr = 0;
// end_addr = 076;
while (!SIGINTreceived && !timeout && !mismatch_count) {
pass_count++;
if (pass_count % 10 == 0)
progress.putf(" %d ", pass_count);
for (cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2)
testwords[cur_test_addr / 2] = random24() & 0xffff;
testwords[cur_test_addr / 2] = random24() & 0xffff; // random
// testwords[cur_test_addr / 2] = (cur_test_addr >> 1) & 0xffff; // linear
progress.put("W"); //info : full memory write
block_wordcount = 97; // something queer
mem_write(arbitration_mode, testwords, start_addr, end_addr, block_wordcount,
&timeout);
mem_access_random(arbitration_mode, UNIBUS_CONTROL_DATO, testwords, start_addr,
end_addr, &timeout, &total_write_block_count);
if (SIGINTreceived || timeout)
break; // leave loop
// first full read
progress.put("R"); //info : full memory write
block_wordcount = 111; // something queer
// read back into unibus_membuffer[]
mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr,
block_wordcount, &timeout);
mem_access_random(arbitration_mode, UNIBUS_CONTROL_DATI, membuffer->data.words,
start_addr, end_addr, &timeout, &total_read_block_count);
// compare
SET_DEBUG_PIN0(0) ;
for (mismatch_count = 0, cur_test_addr = start_addr; cur_test_addr <= end_addr;
cur_test_addr += 2) {
uint16_t cur_mem_val = membuffer->data.words[cur_test_addr / 2];
mismatch = (testwords[cur_test_addr / 2] != cur_mem_val);
if (mismatch) {
SET_DEBUG_PIN0(1) ; // trigger
SET_DEBUG_PIN0(0) ;
ARM_DEBUG_PIN0(1)
; // trigger
ARM_DEBUG_PIN0(0)
;
}
if (mismatch && ++mismatch_count <= MAX_ERROR_COUNT) // print only first errors
printf(
@@ -370,44 +360,7 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode,
printf("Stopped by error: %stimeout, %d mismatches\n", (timeout ? "" : "no "),
mismatch_count);
else
printf("All OK!\n");
printf("All OK! Total %d passes, split into %d block writes and %d block reads\n",
pass_count, total_write_block_count, total_read_block_count);
}
#ifdef USED
/* load a word buffer from file
* words assumed to be little endian (LSB first)
*/
void unibus_c::loadfromfile(char *fname, uint16_t *words, unsigned *bytecount) {
FILE *f;
unsigned n;
f = fopen(fname, "rb");
if (!f) {
ERROR("unibus_loadfromfile(): could not open file \"%s\" for read.", fname);
return;
}
// try to read max address range, shorter files are OK
n = fread((void *) words, 1, 2 * UNIBUS_WORDCOUNT, f);
fclose(f);
*bytecount = n;
INFO("Read %d bytes from \"%s\".", *bytecount, fname);
}
/* save a word buffer to file */
void unibus_c::savetofile(char *fname, uint16_t *words, unsigned bytecount) {
FILE *f;
unsigned n;
f = fopen(fname, "w");
if (!f) {
ERROR("unibus_savetofile(): could not open file \"%s\" for write.", fname);
return;
}
n = fwrite((void *) words, 1, bytecount, f);
if (n != bytecount) {
ERROR("unibus_savetofile(): tried to write %u bytes, only %d successful.", bytecount,
n);
}
fclose(f);
INFO("Wrote %d bytes to \"%s\".", n, fname);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,140 +1,109 @@
/* unibusadapter.hpp: connects multiple "unibusdevices" to the PRU UNIBUS interface
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018-2019 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:
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 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.
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
jul-2019 JH rewrite: multiple parallel arbitration levels
12-nov-2018 JH entered beta phase
*/
#ifndef _UNIBUSADAPTER_HPP_
#define _UNIBUSADAPTER_HPP_
#include <thread>
#include <queue>
#include "iopageregister.h"
#include "priorityrequest.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
class dma_request_c
{
// for each priority arbitration level, theres a table with backplane slots.
// Each device sits in a slot, the slot determinss the request priority within one level (BR4567,NP).
class priority_request_level_c {
public:
dma_request_c(
uint8_t unibus_control,
uint32_t unibus_addr,
uint16_t *buffer,
uint32_t wordcount);
// remember for each backplane slot wether the device has requested
// INTR or DMA at this level
priority_request_c* slot_request[PRIORITY_SLOT_COUNT + 1];
// Optimization to find the high priorized slot in use very fast.
// bit array: bit set -> slot<bitnr> has open request.
uint32_t slot_request_mask;
~dma_request_c();
priority_request_c* active; // request currently handled by PRU, not in table anymore
uint8_t GetUnibusControl() { return _unibus_control; }
uint32_t GetUnibusStartAddr() { return _unibus_start_addr; }
uint16_t* GetBuffer() { return _buffer; }
uint32_t GetWordCount() { return _wordcount; }
uint32_t GetUnibusEndAddr() { return _unibus_end_addr; }
void SetUnibusEndAddr(uint32_t end) { _unibus_end_addr = end; }
bool IsComplete() { return _isComplete; }
bool GetSuccess() { return _success; }
void SetComplete() { _isComplete = true; }
void SetSuccess(bool success) { _success = success; }
private:
uint8_t _unibus_control;
uint32_t _unibus_start_addr;
uint32_t _unibus_end_addr;
uint16_t* _buffer;
uint32_t _wordcount;
bool _isComplete;
bool _success;
void clear();
};
class irq_request_c
{
public:
irq_request_c(
uint32_t level,
uint32_t vector);
~irq_request_c();
uint32_t GetInterruptLevel() { return _level; }
uint32_t GetVector() { return _vector; }
bool IsComplete() { return _isComplete; }
void SetComplete() { _isComplete = true; }
private:
uint32_t _level;
uint32_t _vector;
bool _isComplete;
};
// is a device_c. need a thread (but no params)
class unibusadapter_c: public device_c {
private:
// handle arbitration for each of the 5 request levels in parallel
priority_request_level_c request_levels[PRIORITY_LEVEL_COUNT];
pthread_mutex_t requests_mutex;
void worker_init_event(void);
void worker_power_event(void);
void worker_deviceregister_event(void);
void worker_dma_chunk_complete_event(void);
void worker_intr_complete_event(uint8_t level_index);
void worker(unsigned instance) override; // background worker function
public:
unibusadapter_c();
bool on_param_changed(parameter_c *param) override; // must implement
// list of registered devices.
// Defines GRANT priority:
// Lower index = "nearer to CPU" = higher priority
unibusdevice_c *devices[MAX_DEVICE_HANDLE + 1];
volatile bool line_INIT ; // current state of these UNIBUS signals
volatile bool line_DCLO ;
volatile bool line_INIT; // current state of these UNIBUS signals
volatile bool line_DCLO;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override; // must implement
void on_init_changed(void) override; // must implement
void worker_init_event(void) ;
void worker_power_event(void) ;
void worker_deviceregister_event(void) ;
void worker(void) override; // background worker function
void dma_worker(void); // background DMA worker
bool register_device(unibusdevice_c& device);
void unregister_device(unibusdevice_c& device);
bool request_DMA_active(const char *error_info) ;
bool request_INTR_active(const char *error_info) ;
bool request_client_DMA(uint8_t unibus_control, uint32_t unibus_addr,
uint16_t *buffer, uint32_t wordcount, uint32_t *unibus_end_addr);
void request_INTR(uint32_t level, uint32_t vector);
void rundown_bus_requests(void);
// Helper for request processing
void requests_init(void);
void request_schedule(priority_request_c& request);
void requests_cancel_scheduled(void);
priority_request_c *request_activate_lowest_slot(unsigned level_index);
// bool request_is_active( unsigned level_index);
bool request_is_blocking_active(uint8_t level_index);
void request_active_complete(unsigned level_index);
void request_execute_active_on_PRU(unsigned level_index);
void DMA(dma_request_c& dma_request, bool blocking, uint8_t unibus_control,
uint32_t unibus_addr, uint16_t *buffer, uint32_t wordcount);
void INTR(intr_request_c& intr_request, unibusdevice_register_t *interrupt_register,
uint16_t interrupt_register_value);
void cancel_INTR(intr_request_c& intr_request) ;
void print_shared_register_map(void);
private:
std::queue<dma_request_c*> _dmaRequests;
std::queue<irq_request_c*> _irqRequests;
pthread_t _busWorker_pthread;
pthread_cond_t _busWakeup_cond;
pthread_cond_t _requestFinished_cond;
pthread_mutex_t _busWorker_mutex;
};
extern unibusadapter_c *unibusadapter; // another Singleton

View File

@@ -1,36 +1,38 @@
/* unibusdevice.cpp: abstract device with interface to unibusadapter
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
abstract unibus device
maybe mass storage controller or other device implementing
UNIBUS IOpage registers.
sets device register values depending on internal status,
reacts on register read/write over UNIBUS by evaluation of PRU events.
*/
abstract unibus device
maybe mass storage controller or other device implementing
UNIBUS IOpage registers.
sets device register values depending on internal status,
reacts on register read/write over UNIBUS by evaluation of PRU events.
*/
//#include <string>
//using namespace std;
#include <vector>
#include <assert.h>
#include "logger.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
@@ -39,19 +41,55 @@ unibusdevice_c::unibusdevice_c() :
device_c() {
handle = 0;
register_count = 0;
// device is not yet enabled, UNIBUS properties can be set
base_addr.readonly = false;
intr_vector.readonly = false;
intr_level.readonly = false;
default_base_addr = 0;
default_priority_slot = 0;
default_intr_vector = 0;
default_intr_level = 0;
log_channelmask = 0; // no logging until set
}
unibusdevice_c::~unibusdevice_c() {
}
void unibusdevice_c::install(uint32_t base_addr, unsigned intr_vector, uint8_t intr_level) {
this->base_addr.value = base_addr;
this->intr_vector.value = intr_vector;
this->intr_level.value = intr_level;
// implements params, so must handle "change"
bool unibusdevice_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
// plug/unplug device into UNIBUS:
if (enabled.new_value) {
// enable: lock UNIBUS config
base_addr.readonly = true;
priority_slot.readonly = true;
intr_vector.readonly = true;
intr_level.readonly = true;
install(); // visible on UNIBUS
} else {
// disable
uninstall();
base_addr.readonly = false;
priority_slot.readonly = false;
intr_vector.readonly = false;
intr_level.readonly = false;
}
}
return device_c::on_param_changed(param); // more actions (for enable)
}
// define default values for device BASE address and INTR
void unibusdevice_c::set_default_bus_params(uint32_t default_base_addr, unsigned priority_slot,
unsigned default_intr_vector, unsigned default_intr_level) {
assert(priority_slot <= PRIORITY_SLOT_COUNT); // bitmask!
this->default_base_addr = this->base_addr.value = default_base_addr;
this->default_priority_slot = this->intr_vector.value = priority_slot;
this->default_intr_vector = this->intr_vector.value = default_intr_vector;
this->default_intr_level = this->intr_level.value = default_intr_level;
}
void unibusdevice_c::install(void) {
unibusadapter->register_device(*this); // -> device_c ?
// now has handle
@@ -62,10 +100,6 @@ void unibusdevice_c::install(uint32_t base_addr, unsigned intr_vector, uint8_t i
on_power_changed();
}
void unibusdevice_c::install(void) {
install(default_base_addr, default_intr_vector, default_intr_level);
}
void unibusdevice_c::uninstall(void) {
unibusadapter->unregister_device(*this);
}
@@ -132,13 +166,6 @@ void unibusdevice_c::reset_unibus_registers() {
}
}
// set an UNIBUS interrupt condition with intr_vector and intr_level
void unibusdevice_c::interrupt(void) {
// delegate to unibusadapter_c
unibusadapter->request_INTR(intr_level.value, intr_vector.value);
// WARNING("unibusdevice_c::interrupt() TODO: generated interrupt!");
}
// log register state changes:
// print event info
// - print full register content to logger
@@ -184,3 +211,89 @@ void unibusdevice_c::log_register_event(const char *change_info,
}
DEBUG(buffer);
}
// search device in global list mydevices[]
unibusdevice_c *unibusdevice_c::find_by_request_slot(uint8_t priority_slot) {
list<device_c *>::iterator devit;
for (devit = device_c::mydevices.begin(); devit != device_c::mydevices.end(); ++devit) {
unibusdevice_c *ubdevice = dynamic_cast<unibusdevice_c *>(*devit);
if (ubdevice) {
// all dma and intr requests
for (vector<dma_request_c *>::iterator reqit = ubdevice->dma_requests.begin();
reqit < ubdevice->dma_requests.end(); reqit++)
if ((*reqit)->get_priority_slot() == priority_slot)
return ubdevice;
for (vector<intr_request_c *>::iterator reqit = ubdevice->intr_requests.begin();
reqit < ubdevice->intr_requests.end(); reqit++)
if ((*reqit)->get_priority_slot() == priority_slot)
return ubdevice;
}
}
return NULL;
}
// returns a string of form
// reg_first-reg_last, slots from-to, DMA, INTR level1/vec1,level2/vec2,...
char *unibusdevice_c::get_unibus_resource_info(void) {
static char buffer[1024];
char tmpbuff[256];
buffer[0] = 0;
// get register address range
// use parameter "base_addr", register struct only valid after unibusadapter.install()
if (register_count == 0) // cpu is a device without register interface
strcpy(tmpbuff, "");
else if (register_count == 1)
sprintf(tmpbuff, "addr %06o", base_addr.value);
else
sprintf(tmpbuff, "addr %06o-%06o (%d regs)", base_addr.value, base_addr.value+2*(register_count-1), register_count) ;
strcat(buffer, tmpbuff);
// get priority slot range from DMA request and intr_requests
uint8_t slot_from = 0xff, slot_to = 0;
for (vector<dma_request_c *>::iterator it = dma_requests.begin(); it < dma_requests.end();
it++) {
slot_from = std::min(slot_from, (*it)->get_priority_slot());
slot_to = std::max(slot_to, (*it)->get_priority_slot());
}
for (vector<intr_request_c *>::iterator it = intr_requests.begin();
it < intr_requests.end(); it++) {
slot_from = std::min(slot_from, (*it)->get_priority_slot());
slot_to = std::max(slot_to, (*it)->get_priority_slot());
}
if (slot_from > slot_to) // no requests: use devcie parameter
slot_from = slot_to = priority_slot.value;
if (slot_from == slot_to)
sprintf(tmpbuff, ", slot %u", (unsigned) slot_from);
else
sprintf(tmpbuff, ", slots %u-%u", (unsigned) slot_from, (unsigned) slot_to);
strcat(buffer, tmpbuff);
// DMA channels
if (dma_requests.size() > 0) {
if (dma_requests.size() == 1)
sprintf(tmpbuff, ", DMA");
else
sprintf(tmpbuff, ", %uxDMA", dma_requests.size());
strcat(buffer, tmpbuff);
}
// Interrupts
if (intr_requests.size() > 4) {
// that crazy testcontroller has 31*4 !
sprintf(tmpbuff, "%d INTRs", intr_requests.size());
strcat(buffer, tmpbuff);
} else if (intr_requests.size() > 0) {
const char *sep = ":";
strcat(buffer, ", INTRs");
for (vector<intr_request_c *>::iterator it = intr_requests.begin();
it < intr_requests.end(); it++) {
sprintf(tmpbuff, "%s%d/%03o", sep, (*it)->get_level(), (*it)->get_vector());
strcat(buffer, tmpbuff);
sep = ",";
}
}
return buffer;
}

View File

@@ -1,28 +1,28 @@
/* unibusdevice.hpp: abstract device with interface to unibusadapter
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _UNIBUSDEVICE_HPP_
#define _UNIBUSDEVICE_HPP_
@@ -33,12 +33,15 @@
#include "device.hpp"
#include "iopageregister.h"
#include "priorityrequest.hpp"
// forwards
class unibusdevice_c;
typedef struct {
typedef struct unibusdevice_register_struct {
// backlink
unibusdevice_c *device;
char name[40] ; // for display
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
@@ -77,26 +80,49 @@ typedef struct {
} 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
// 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 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", 3, 8);
// !!! slot, vector, level READONLY. If user shoudl 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;
unsigned default_intr_vector;
unsigned default_intr_level;
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
@@ -104,35 +130,25 @@ public:
unsigned register_count;
unibusdevice_register_t registers[MAX_REGISTERS_PER_DEVICE];
unsigned log_channelmask ; // channelmask for DEBUG logging
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
// setup address tables, also in shared memory
// start both threads
void install(uint32_t base_addr, unsigned intr_vector, uint8_t intr_level);
void install(void); // defaults
void uninstall(void);
bool is_installed() {
return (handle > 0);
}
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);
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) ;
// set an UNIBUS interrupt condition with intr_vector and intr_level
void interrupt(void) ;
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!
@@ -146,8 +162,9 @@ public:
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);
void log_register_event(const char *change_info, unibusdevice_register_t *changed_reg) ;
char *get_unibus_resource_info(void) ;
};

View File

@@ -1,29 +1,29 @@
/* utils.cpp: misc. utilities
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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-may-2018 JH created
*/
12-nov-2018 JH entered beta phase
20-may-2018 JH created
*/
#define _UTILS_CPP_
@@ -31,8 +31,11 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <signal.h>
#include <stdbool.h>
#include <ctype.h>
#include <assert.h>
#include <time.h>
#include <limits.h>
#include <sys/time.h>
@@ -46,13 +49,12 @@
using namespace std;
/*********************************
* strcpy without buffer overlfow
*/
void strcpy_s(char *dest, int len, const char *src) {
strncpy(dest, src, len-1) ;
dest[len-1] = 0 ; // termiante if truncated
strncpy(dest, src, len - 1);
dest[len - 1] = 0; // termiante if truncated
}
/*********************************
@@ -72,25 +74,48 @@ void SIGINTcatchnext() {
SIGINTreceived = 0;
}
void break_here(void) {}
void break_here(void) {
}
/*** time measuring ***/
timeout_c::timeout_c() {
log_label = "TO" ;
log_label = "TO";
}
void timeout_c::start(uint64_t duration_ns) {
uint64_t timeout_c::get_resolution_ns() {
struct timespec res;
clock_getres(CLOCK_MONOTONIC, &res);
return BILLION * res.tv_sec + res.tv_nsec;
}
void timeout_c::start_ns(uint64_t duration_ns) {
this->duration_ns = duration_ns;
clock_gettime(CLOCK_MONOTONIC, &starttime);
}
void timeout_c::start_us(uint64_t duration_us) {
start_ns(duration_us * 1000);
}
void timeout_c::start_ms(uint64_t duration_ms) {
start_ns(duration_ms * MILLION);
}
uint64_t timeout_c::elapsed_ns(void) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return BILLION * (now.tv_sec - starttime.tv_sec) + now.tv_nsec - starttime.tv_nsec;
uint64_t result = (uint64_t) BILLION * (now.tv_sec - starttime.tv_sec)
+ (uint64_t) now.tv_nsec - starttime.tv_nsec;
return result;
}
uint64_t timeout_c::elapsed_us(void) {
return elapsed_ns() / 1000;
}
uint64_t timeout_c::elapsed_ms(void) {
return elapsed_ns() / MILLION;
}
bool timeout_c::reached() {
@@ -102,7 +127,7 @@ void timeout_c::wait_ns(uint64_t duration_ns) {
struct timespec ts = { (long) (duration_ns / BILLION), (long) (duration_ns % BILLION) };
int res = nanosleep(&ts, NULL);
if (res)
DEBUG("nanosleep() return a %d", res) ;
DEBUG("nanosleep() return a %d", res);
}
// wait a number of milliseconds
@@ -126,24 +151,68 @@ void progress_c::init(unsigned linewidth) {
}
void progress_c::put(const char *info) {
printf("%s", info);
cur_col += strlen(info);
if (cur_col >= linewidth) {
printf("\n");
cur_col = 0;
cur_col = strlen(info);
}
printf("%s", info);
fflush(stdout);
}
void progress_c::putf(const char *fmt, ...) {
static char buffer[256];
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsprintf(buffer, fmt, arg_ptr);
va_end(arg_ptr);
put(buffer);
}
/* random number with 24 valid bits
* RAND_MAX is only guaranteed 15 bits
*/
unsigned random24() {
unsigned val;
assert(RAND_MAX >= 0x3fff);
val = rand() ^ (rand() << 9);
return val & 0xffffff;
}
/* random numbers, distributed logarithmically
* returns 0..limit-1
*/
uint32_t random32_log(uint32_t limit) {
uint32_t result, mantissa;
int rand_exponent, limit_exp;
assert(limit > 0);
assert(RAND_MAX >= 0x3fff); // 15 bits
// generate normalized mantissa, bit 31 set
mantissa = rand();
mantissa ^= (rand() << 9);
mantissa ^= (rand() << 18);
while ((mantissa & (1 << 31)) == 0)
mantissa <<= 1;
// rand_exponent of limit: 2^limit_exp <= limit
// ctz = "Count Leading Zeros"
// limit = 1 -> exp=0, limit = 0xffffffff -> exp = 31
limit_exp = 31 - __builtin_clz(limit);
limit_exp++; // 2^limit_exp >= limit
// random rand_exponent 0..limit-1
rand_exponent = rand() % limit_exp;
// 2^rand_exponent <= limit
result = mantissa >> (31 - rand_exponent);
// mantissa has bit 31 set, is never shifted more then 31
assert(result);
// final masking
if (limit > 1)
result %= limit;
return result;
}
char *cur_time_text() {
static char result[80], millibuff[10];
timeval cur_time;
@@ -166,17 +235,16 @@ bool fileExists(const std::string& filename) {
// Generates "perror()" printout,
// msgfmt must have one "%s" field for absolute filename
char *fileErrorText(const char *msgfmt, const char *fname) {
static char linebuff[PATH_MAX +100];
char abspath[PATH_MAX] ;
static char linebuff[PATH_MAX + 100];
char abspath[PATH_MAX];
realpath(fname, abspath);
sprintf(linebuff, msgfmt, abspath);
strcat(linebuff, ": ") ;
strcat (linebuff, strerror(errno)) ;
strcat(linebuff, ": ");
strcat(linebuff, strerror(errno));
// perror(linebuff);
return linebuff ;
return linebuff;
}
// add a number of microseconds to a time
struct timespec timespec_add_us(struct timespec ts, unsigned us) {
ts.tv_nsec += us * 1000;
@@ -204,3 +272,102 @@ struct timespec timespec_future_us(unsigned offset_us) {
}
*/
// decodes C escape sequences \char, \nnn octal, \xnn hex
// result string is smaller or same as "encoded", must have at least "ncoded" size
// return: true of OK, else false
static int digitval(char c) {
c = toupper(c);
if (c < '0')
return 0; // illegal
else if (c <= '9')
return c - '0';
else if (c < 'A')
return 0; // illegal
else if (c <= 'F')
return c - 'A' + 10;
else
return 0; // illegal
}
bool str_decode_escapes(char *result, unsigned result_size, char *encoded) {
int c ;
char *wp = result; // write pointer
char *rp = encoded; // read pointer
assert(result_size >= strlen(encoded));
while (*rp) {
if (*rp != '\\') {
*wp++ = *rp++; // not escaped
continue;
}
// decode escapes
rp++; // eat backslash
int n = strspn(rp, "01234567"); //
if (n >= 1) { // \nnn given
// use max 3 digits for octal literal
c = digitval(*rp++) ;
if (n >= 2)
c = c * 8 + digitval(*rp++) ;
if (n >= 3)
c = c * 8 + digitval(*rp++) ;
*wp++ = (char) c;
continue;
}
switch (*rp) {
// literals allowed behind backslash
case '\'':
case '"':
case '?':
case '\\':
*wp++ = *rp++;
continue;
case 'a':
*wp++ = 0x07; // audible bell
rp++;
continue;
case 'b':
*wp++ = 0x08; // backspace
rp++;
continue;
case 'f':
*wp++ = 0x0c; // form feed - new page
rp++;
continue;
case 'n':
*wp++ = 0x0a; // line feed - new line
rp++;
continue;
case 'r':
*wp++ = 0x0d; // carriage return
rp++;
continue;
case 't':
*wp++ = 0x09; // horizontal tab
rp++;
continue;
case 'v':
*wp++ = 0x0b; // vertical tab
rp++;
continue;
case 'x': // hex: \xnn
rp++; // eat "x"
// in contrast to the standard, max 2 hex digits are evaualted, not arbitrary amount.
// this makes it easy to write "L 200" as "L\x20200".
// Else \xnnnn may eat following chars not meant as part of the hex sequence
// convert and skip arbitrary count of hex characters
n = strspn(rp, "0123456789aAbBcCdDeEfF");
if (n < 1)
return false ; // no hexdigit after "x"
// use max 2 digits for hex literal
c = digitval(toupper(*rp++)) ;
if (n >= 2)
c = c * 16 + digitval(toupper(*rp++)) ;
// c = strtol(rp, &rp, 16) ; if unlimited hex chars
*wp++ = (char) c;
continue;
default:
return false; // unknown char behind backslash
}
}
*wp = 0;
return true;
}

View File

@@ -1,29 +1,29 @@
/* utils.hpp: misc. utilities
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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-may-2018 JH created
*/
12-nov-2018 JH entered beta phase
20-may-2018 JH created
*/
#ifndef _UTILS_HPP_
#define _UTILS_HPP_
@@ -40,13 +40,14 @@
#define MILLION 1000000L
#define BILLION (1000L * MILLION)
#define BIT(n) (1 << (n))
#ifndef _UTILS_CPP_
extern volatile int SIGINTreceived;
#endif
// my version
void strcpy_s(char *dest, int len, const char *src) ;
void strcpy_s(char *dest, int len, const char *src);
// mark unused parameters
#define UNUSED(x) (void)(x)
@@ -56,23 +57,28 @@ void strcpy_s(char *dest, int len, const char *src) ;
void SIGINTcatchnext();
// dummy to have an executable line for break points
void break_here(void) ;
void break_here(void);
class timeout_c: public logsource_c {
private:
struct timespec starttime;
uint64_t duration_ns;
public:
timeout_c() ;
void start(uint64_t duration_ns);
uint64_t elapsed_ns(void);bool reached(void);
timeout_c();
uint64_t get_resolution_ns(void) ;
void start_ns(uint64_t duration_ns);
void start_us(uint64_t duration_us) ;
void start_ms(uint64_t duration_ms) ;
uint64_t elapsed_ns(void);
uint64_t elapsed_us(void);
uint64_t elapsed_ms(void);
bool reached(void);
void wait_ns(uint64_t duration_ns);
void wait_us(unsigned duration_us);
void wait_ms(unsigned duration_ms);
};
class progress_c {
private:
unsigned linewidth;
@@ -81,27 +87,32 @@ public:
progress_c(unsigned linewidth);
void init(unsigned linewidth);
void put(const char *info);
void putf(const char *fmt, ...);
};
unsigned random24();
unsigned random24(void);
uint32_t random32_log(uint32_t limit);
char *cur_time_text(void) ;
char *cur_time_text(void);
// remove leading/trailing spaces
// https://stackoverflow.com/questions/83439/remove-spaces-from-stdstring-in-c
#define TRIM_STRING(str) str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end())
bool fileExists(const std::string& filename) ;
bool fileExists(const std::string& filename);
char * fileErrorText(const char *msgfmt, const char *fname) ;
char * fileErrorText(const char *msgfmt, const char *fname);
//ool caseInsCompare(const std::string& s1, const std::string& s2) ;
// add a number of microseconds to a time
struct timespec timespec_add_us(struct timespec ts, unsigned us) ;
struct timespec timespec_add_us(struct timespec ts, unsigned us);
// add microseconds to current time
struct timespec timespec_future_us(unsigned offset_us) ;
struct timespec timespec_future_us(unsigned offset_us);
// decodes C escape sequences \char, \nnn octal, \xnn hex
bool str_decode_escapes(char *result, unsigned result_size, char *encoded) ;
#endif /* _UTILS_H_ */

View File

@@ -46,7 +46,8 @@ CFLAGS_OPTIMIZER=--opt_level=3 --opt_for_speed=5 --auto_inline --c_src_interli
#CFLAGS_OPTIMIZER=--opt_level=3 --auto_inline --c_src_interlist --optimizer_interlist --gen_opt_info=2
#Common compiler and linker flags (Defined in 'PRU Optimizing C/C++ Compiler User's Guide)
CFLAGS=-v3 $(CFLAGS_OPTIMIZER) \
--display_error_number --endian=little --hardware_mac=on --obj_directory=$(OBJ_DIR) --pp_directory=$(OBJ_DIR) -ppd -ppa
--display_error_number --emit_warnings_as_errors --verbose_diagnostics \
--endian=little --hardware_mac=on --obj_directory=$(OBJ_DIR) --pp_directory=$(OBJ_DIR) -ppd -ppa
#Linker flags (Defined in 'PRU Optimizing C/C++ Compiler User's Guide)
LFLAGS=--reread_libs --warn_sections --stack_size=$(STACK_SIZE) --heap_size=$(HEAP_SIZE)
@@ -74,6 +75,7 @@ OBJECTS_COMMON= \
$(OBJ_DIR)/pru1_statemachine_intr.object \
$(OBJ_DIR)/pru1_statemachine_powercycle.object \
$(OBJ_DIR)/pru1_statemachine_slave.object \
$(OBJ_DIR)/pru1_timeouts.object \
$(OBJ_DIR)/pru1_utils.object

View File

@@ -1,38 +1,37 @@
/* pru1_arm_mailbox.c: datastructures common to ARM and PRU
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#include <stdint.h>
#define _MAILBOX_C_
#include "mailbox.h"
// Place PRU-ARM mailbox struct at begin of PRU_SHAREDMEM
// (shared 12KB memory)
#pragma DATA_SECTION(mailbox,".mailbox_arm_sec")
volatile far mailbox_t mailbox ;
volatile far mailbox_t mailbox;

View File

@@ -352,8 +352,6 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
// spruh73n, chapter 4.4.1.2.3.2,
CT_CFG.GPCFG1_bit.PRU1_GPI_MODE = 0;
DEBUG_PIN_SET(0); // clear, no error
#ifdef TEST_66MHZ
while (1) {
__R30 |= (1 << 12); // set PRU1.12
@@ -396,16 +394,16 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
uint8_t resvar;
// echo DATA0 read only
buslatches_test_get(2,resvar);
DEBUG_PIN_SET(buslatches_getbyte(2) != a);
PRU_DEBUG_PIN(buslatches_getbyte(2) != a);
// buslatches_debug_set(resvar & 1);
buslatches_test_get(3,resvar);
DEBUG_PIN_SET(buslatches_getbyte(3) != b);
PRU_DEBUG_PIN(buslatches_getbyte(3) != b);
//buslatches_debug_set(resvar & 1);
buslatches_test_get(5,resvar);
DEBUG_PIN_SET(buslatches_getbyte(5) != c);
PRU_DEBUG_PIN(buslatches_getbyte(5) != c);
//buslatches_debug_set(resvar & 1);
buslatches_test_get(6,resvar);
DEBUG_PIN_SET(buslatches_getbyte(6) != d);
PRU_DEBUG_PIN(buslatches_getbyte(6) != d);
//buslatches_debug_set(resvar & 1);
}
#endif
@@ -423,16 +421,16 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
buslatches_setbyte(5, c)
;
if (buslatches_getbyte(2) != a)
DEBUG_PIN_PULSE_100NS
PRU_DEBUG_PIN_PULSE_100NS
;// show error flag. cleared by next reg_sel
buslatches_setbyte(6, d)
;
if (buslatches_getbyte(3) != b)
DEBUG_PIN_PULSE_100NS;
PRU_DEBUG_PIN_PULSE_100NS;
if (buslatches_getbyte(5) != c)
DEBUG_PIN_PULSE_100NS;
PRU_DEBUG_PIN_PULSE_100NS;
if (buslatches_getbyte(6) != d)
DEBUG_PIN_PULSE_100NS;
PRU_DEBUG_PIN_PULSE_100NS;
a++;
b++;
c++;

View File

@@ -69,7 +69,6 @@ extern buslatches_t buslatches;
(__R31 & 0xff) \
)
// identify register which must be set byte-wise
#define BUSLATCHES_REG_IS_BYTE(reg_sel) ( \
((reg_sel) == 2) || ((reg_sel) == 3) || ((reg_sel) == 5) || ((reg_sel) == 6) \
@@ -133,7 +132,7 @@ void buslatches_reset(void);
void buslatches_powercycle(void);
void buslatches_exerciser(void) ;
void buslatches_exerciser(void);
void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d);

View File

@@ -1,52 +0,0 @@
Speed up busaltch access with overlapping access
to 3:8 mux
On read:
macro buslatches_get(cycles_since_regmux, reg_sel, reg_sel_next, result_var, reg_sel_current_var)
R1 - check if 74138 regmux is already set as expected (vvariable "reg_sel_current")
if yes:
R21 wait until required delay to last mux switch is reached
Cycle time since last access to regmux_sel in "cycles_since_regmux")
(read_ 10 cycles, 6 alone for BBB signal processing)
if no:
R31 - set mux
R32 wait full delay (10 cycles)
R22- read in muxed data from lvt541
result_var := R31
R4- set next mux expected value "reg_sel_next"
R5 update reg_sel_current
On write: macro buslatches_set(cycles_since_regmux, reg_sel, reg_sel_next, val, reg_sel_current_variable)
W1 - setup data do PRU0 outputs
W2 - set regmux, update reg_sel_current
Dont check if 74138 regmux is already set as exepected,
Setting of output data on PRU0 uses moretime than set up uf regmux and
progation of selct singals to 74371 latches (only 10ns!)
Code path:
- check needed, if reg_mux is as set by previous busaltch_get/set()
- checks static, may be elimianted by compiler.
- if reg_sel_next is a local var, it may be eliminated totally
- if reg_sel_next is a global var, expected "next" value in different state machnies
may be read from logic analyzer trace of "typical" operation.
- a local variable "cycles_since_last_regmux" may be used to
count processing time. It is only set with const vlaues,
so may be elimated by compiler and may be used in delay_cycles()
uint8_t reg_sel_current ; // local var
reg_sel_current = 0xff
a0_7 = buslatches_get(0, 4, 5, reg_sel_current) ;
// => if (4 != 0xff) -> code R31,R32, wait 10 cycles
a8_15 = buslatches_get(4, 5, 2, reg_sel_current) ;
if (5 != 5) => mux already correct,
R21: delaycycles(10-4), // estimated 4 cycles processing time

View File

@@ -1,28 +1,28 @@
/* pru1_ddrmem.c: Control the shared DDR RAM - used for UNIBUS memory
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#include <stdint.h>
#include <string.h>
@@ -33,8 +33,8 @@
// fill whole memory with pattern
// called by mailbox command ARM2PRU_REQUEST_DDR_FILL_PATTERN
void ddrmem_fill_pattern(void) {
unsigned n ;
unsigned n;
volatile uint16_t *wordaddr = mailbox.ddrmem_base_physical->memory.words;
for (n = 0; n < UNIBUS_WORDCOUNT ; n++)
*wordaddr++ = n ;
for (n = 0; n < UNIBUS_WORDCOUNT; n++)
*wordaddr++ = n;
}

View File

@@ -1,28 +1,28 @@
/* pru1_iopageregisters.c: handle UNIBUS behaviour of emulated devices
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#define _IOPAGEREGISTERS_C_
@@ -30,7 +30,7 @@
#include <stdint.h>
#include "pru1_utils.h"
#include "pru1_buslatches.h" // DEBUG_PIN_SET
#include "pru1_buslatches.h" // PRU_DEBUG_PIN
#include "mailbox.h"
#include "iopageregister.h"
#include "ddrmem.h"
@@ -42,7 +42,6 @@
// not volatile: data seldom changed by ARM, speed matters!
iopageregisters_t deviceregisters;
/* request value from a device register
* page_table_entry already calculated for addr
* may have side effects onto other registers!
@@ -52,26 +51,27 @@ iopageregisters_t deviceregisters;
* for post processing. SSYN must remain asserted until ARM is complete
*/
uint8_t iopageregisters_read(uint32_t addr, uint16_t *val) {
uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr);
uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr);
if (page_table_entry == PAGE_MEMORY) {
// addr in allowed 18bit memory range, not in I/O page
*val = DDRMEM_MEMGET_W(addr);
return 1;
} else if (page_table_entry == PAGE_IO) {
// uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)];
uint8_t reghandle ;
reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ;
uint8_t reghandle;
reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr);
if (!reghandle) {
return 0; // register not implemented as "active"
}
}
// return register value. remove "volatile" attribute
// DEBUG_PIN_SET(1) ;
// indexing this records takes 4,6 us, if record size != 8
// indexing this records takes 4,6 us, if record size != 8
iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias
// DEBUG_PIN_SET(0) ;
*val = reg->value;
if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATI)
DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATI, addr, *val);
// ARM is clearing this, while SSYN asserted, so no concurrent next bus cycle.
// no concurrent ARP+PRU access
return 1;
} else
return 0;
@@ -83,7 +83,7 @@ uint8_t iopageregisters_read(uint32_t addr, uint16_t *val) {
* may set mailbox event to ARM, then SSYN must remain asserted until ARM is complete
*/
uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) {
uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr);
uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr);
if (page_table_entry == PAGE_MEMORY) {
// addr in allowed 18bit memory range, not in I/O page
// no check wether addr is even (A00=0)
@@ -92,13 +92,13 @@ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) {
return 1;
} else if (page_table_entry == PAGE_IO) {
// uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)];
uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ;
uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr);
if (!reghandle)
return 0; // register not implemented
// change register value
iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias
uint16_t reg_val = (reg->value & ~reg->writable_bits) | (w & reg->writable_bits);
reg->value = reg_val ;
reg->value = reg_val;
if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATO)
DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATO, addr, reg_val);
return 1;
@@ -107,29 +107,29 @@ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) {
}
uint8_t iopageregisters_write_b(uint32_t addr, uint8_t b) {
uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr);
uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr);
if (page_table_entry == PAGE_MEMORY) {
// addr in allowed 18bit memory range, not in I/O page
DDRMEM_MEMSET_B(addr, b);
return 1;
} else if (page_table_entry == PAGE_IO) {
// uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)];
uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ;
uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr);
if (!reghandle)
return 0; // register not implemented
// change register value
iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias
uint16_t reg_val ;
uint16_t reg_val;
if (addr & 1) // odd address = write upper byte
reg_val = (reg->value & 0x00ff) // don't touch lower byte
| (reg->value & ~reg->writable_bits & 0xff00) // protected upper byte bits
| (((uint16_t)b << 8) & reg->writable_bits); // changed upper byte bits
| (((uint16_t) b << 8) & reg->writable_bits); // changed upper byte bits
else
// even address: write lower byte
reg_val = (reg->value & 0xff00) // don' touch upper byte
| (reg->value & ~reg->writable_bits & 0x00ff) // protected upper byte bits
| (b & reg->writable_bits); // changed lower byte bits
reg->value = reg_val ;
reg->value = reg_val;
if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATO)
DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATOB, addr, reg_val);
return 1;

View File

@@ -38,10 +38,12 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"
#include "pru1_utils.h"
#include "pru1_timeouts.h"
#include "pru_pru_mailbox.h"
#include "mailbox.h"
@@ -56,22 +58,26 @@
#include "pru1_statemachine_init.h"
#include "pru1_statemachine_powercycle.h"
// Supress warnings about using void * as function pointers
// sm_slave_state = (statemachine_state_func)&sm_slave_start;
// while (sm_slave_state = sm_slave_state()) << usage
#pragma diag_push
#pragma diag_remark=515
void main(void) {
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
timeout_init();
// clear all tables, as backup if ARM fails todo
iopageregisters_init();
buslatches_reset(); // all deasserted
// init mailbox
mailbox.arm2pru_req = ARM2PRU_NONE;
mailbox.events.eventmask = 0;
mailbox.events.initialization_signals_prev = 0;
mailbox.events.initialization_signals_cur = 0;
memset((void *) &mailbox, 0, sizeof(mailbox));
while (1) {
// display opcode (active for one cycle
@@ -99,7 +105,7 @@ void main(void) {
__halt(); // that's it
break;
#ifdef USED
case ARM2PRU_MAILBOXTEST1:
case ARM2PRU_MAILBOXTEST1:
// simulate a register read access.
#ifdef TEST_TIMEOUT
while (1) {
@@ -120,8 +126,8 @@ void main(void) {
// pru_pru_mailbox.pru0_r30 = mailbox.mailbox_test.addr & 0xff;
// __R30 = (mailbox.mailbox_test.addr & 0xf) << 8;
mailbox.mailbox_test.val = mailbox.mailbox_test.addr;
__R30 = (mailbox.arm2pru_req & 0xf) << 8; // optical ACK
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
__R30 = (mailbox.arm2pru_req & 0xf) << 8;// optical ACK
mailbox.arm2pru_req = ARM2PRU_NONE;// ACK: done
break;
#endif
case ARM2PRU_BUSLATCH_INIT: // set all mux registers to "neutral"
@@ -151,10 +157,10 @@ void main(void) {
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
}
case ARM2PRU_BUSLATCH_EXERCISER: // exercise 8 byte accesses to mux registers
buslatches_exerciser() ;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break ;
case ARM2PRU_BUSLATCH_EXERCISER: // exercise 8 byte accesses to mux registers
buslatches_exerciser();
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_BUSLATCH_TEST: {
buslatches_test(mailbox.buslatch_test.addr_0_7, mailbox.buslatch_test.addr_8_15,
@@ -163,24 +169,26 @@ void main(void) {
break;
}
case ARM2PRU_INITPULSE: // generate a pulse on UNIBUS INIT
// INIT: latch[7], bit 3
// INIT: latch[7], bit 3
buslatches_setbits(7, BIT(3), BIT(3)); // assert INIT
__delay_cycles(MILLISECS(250)); // INIT is 250ms
buslatches_setbits(7, BIT(3), 0); // deassert INIT
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_POWERCYCLE: // do ACLO/DCLO power cycle
buslatches_powercycle();
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_DMA_ARB_NONE:
sm_dma_start(); // without NPR/NPG arbitration
case ARM2PRU_DMA: {
// without NPR/NPG arbitration
statemachine_state_func sm_dma_state = (statemachine_state_func) &sm_dma_start;
// simply call current state function, until stopped
// parallel the BUS-slave statemachine is triggered
// by master logic.
while (!sm_dma.state())
while (sm_dma_state = sm_dma_state())
;
}
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_DDR_FILL_PATTERN:
@@ -197,9 +205,10 @@ void main(void) {
// do UNIBUS slave cycles, until ARM abort this by
// writing into mailbox.arm2pru_req
while (mailbox.arm2pru_req == ARM2PRU_DDR_SLAVE_MEMORY) {
sm_slave_start();
statemachine_state_func sm_slave_state =
(statemachine_state_func) &sm_slave_start;
// do all states of an access, start when MSYN found.
while (!sm_slave.state())
while (sm_slave_state = sm_slave_state())
;
}
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
@@ -207,4 +216,5 @@ void main(void) {
} // switch
} // while
}
#pragma diag_pop

View File

@@ -48,6 +48,7 @@
#include "resource_table_empty.h"
#include "pru1_utils.h"
#include "pru1_timeouts.h"
#include "pru_pru_mailbox.h"
#include "mailbox.h"
@@ -62,148 +63,198 @@
#include "pru1_statemachine_init.h"
#include "pru1_statemachine_powercycle.h"
// supress warnigns about using void * as function pointers
// sm_slave_state = (statemachine_state_func)&sm_slave_start;
// while (sm_slave_state = sm_slave_state()) << usage
#pragma diag_push
#pragma diag_remark=515
/***
3 major states executed in circular 1- 2- 3 order.
1. "SLAVE":
UniBone monitoring BUS traffic as slave for DATI/DATO cycles
Watch UNIBUS for CPU access to emulated memory/devices
High speed not necessary: Bus master will wait with MSYN if UniBone not responding.
wathcin BG/BPG signals, catching requested GRANts and forwardinf
other GRANTS
- monitorinf INIT and AC_LO/DC_LO
- watching fpr AMR2PRU commands
2. "BBSYWAIT": UNibone got PRIORITY GRAMT, has set SACK and released BR/NPR
waits for current BUS master to relaeasy BBSY (ony DATI/DATO cycle max)
- SACK active: no GRANT forward necessary, no arbitration necessary
- INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary
3. "MASTER": UniBone is Bus master: transfering INTR vector or doing DMA
- Own timing: transfer DMA data block or INTR vector with master cycles
- SACK active: no GRANT forward necessary, no arbitration necessary
- INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary
*/
void main(void) {
// state function pointer for different state machines
statemachine_arb_worker_func sm_arb_worker = &sm_arb_worker_client;
statemachine_state_func sm_data_slave_state = NULL;
statemachine_state_func sm_data_master_state = NULL;
statemachine_state_func sm_init_state = NULL;
statemachine_state_func sm_powercycle_state = NULL;
// these are function pointers: could be 16bit on PRU?
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
timeout_init();
// clear all tables, as backup if ARM fails todo
iopageregisters_init();
buslatches_reset(); // all deasserted, caches cleared
/* ARM must init mailbox, especially:
mailbox.arm2pru_req = ARM2PRU_NONE;
mailbox.events.eventmask = 0;
mailbox.events.initialization_signals_prev = 0;
mailbox.events.initialization_signals_cur = 0;
*/
/* start parallel emulation of all devices,
* Process __DMA and _INTR bus master operations
*
* ! Several state machines (DMA, Powercycle, INIT,) use the same global timeout.
* ! Never execute these in parallel !
mailbox.arm2pru_req = ARM2PRU_NONE;
mailbox.events.eventmask = 0;
mailbox.events.initialization_signals_prev = 0;
mailbox.events.initialization_signals_cur = 0;
*/
// Reset PDP-11 with power-cycle simulation.
// Necessary, as until now NPR/NPG/BG/BR/SACK lines were "unconnected"
// buslatches_powercycle();
// __delay_cycles(MILLISECS(100));
// execute 2x, because M9312 boot ROMs need this
// __delay_cycles(MILLISECS(250));
// buslatches_powercycle();
// buslatches_pulse_debug ;
// base operation: accept and execute slave cycles
sm_slave_start();
sm_arb_reset();
while (true) {
uint32_t arm2pru_req_cached;
// do all states of an access, start when MSYN found.
uint8_t arm2pru_req_cached;
// slave cycles may trigger events to ARM, which changes "active" registers
// and issues interrupts
while (!sm_slave.state())
; // execute complete slave cycle, then check NPR/INTR
if (sm_data_master_state == NULL) {
// State 1 "SLAVE"
// fast: a complete slave data cycle
if (!sm_data_slave_state)
sm_data_slave_state = (statemachine_state_func) &sm_slave_start;
while ((sm_data_slave_state = sm_data_slave_state()) && !mailbox.events.event_deviceregister)
// throws signals to ARM,
// Acess to interna lregsitres may may issue AMR2PRU opcode, so exit loop then
;// execute complete slave cycle, then check NPR/INTR
// update state of init lines
// INIT never asserted in the midst of a transaction, bit 3,4,5
do_event_initializationsignals();
// one phase of INIT or power cycle
if (sm_powercycle_state)
sm_powercycle_state = sm_powercycle_state();
else if (sm_init_state)
// init only if no power cycle, power cycle overrides INIT
sm_init_state = sm_init_state();
// standard operation may be interrupt by other requests
arm2pru_req_cached = mailbox.arm2pru_req;
switch (arm2pru_req_cached) {
case ARM2PRU_NONE:
// pass BG[4-7] to next device, state machine "idle"
// pass all Arbitration GRANT IN to GRANT OUT for next device.
// This is not necessary while INTR or DMA is actiove:
// signal INT or PWR FAIL to ARM
do_event_initializationsignals();
// Priority Arbitration
// execute one of the arbitration workers
uint8_t grant_mask = sm_arb_worker();
// sm_arb_worker()s include State 2 "BBSYWAIT".
// So now SACK maybe set, even if grant_mask is still 0
if (grant_mask & PRIORITY_ARBITRATION_BIT_NP) {
sm_data_master_state = (statemachine_state_func) &sm_dma_start;
// can data_master_state be overwritten in the midst of a running data_master_state ?
// no: when running, SACK is set, no new GRANTs
} else if (grant_mask & PRIORITY_ARBITRATION_INTR_MASK) {
// convert bit in grant_mask to INTR index
uint8_t idx = PRIORITY_ARBITRATION_INTR_BIT2IDX(grant_mask);
// now transfer INTR vector for interupt of GRANted level.
// vector and ARM context have been setup by ARM before ARM2PRU_INTR already
sm_intr.vector = mailbox.intr.vector[idx];
sm_intr.level_index = idx; // to be returned to ARM on complete
sm_data_master_state = (statemachine_state_func) &sm_intr_start;
}
} else {
// State 3 "MASTER"
// we have been GRANTed bus mastership and are doing DMA or INTR
// SACK held here -> no further arbitration
// INTR is only 1 cycle, DMA has SACK set all the time, arbitration
// prohibited then.
sm_arb_state_idle();
// do only forward GRANT lines if not INTR is pending,
// else our GRANT would be passed too.
break; // fast case: only slave operation
case ARM2PRU_NOP: // needed to probe PRU run state
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_DMA_ARB_NONE: // ignore SACK condition
case ARM2PRU_DMA_ARB_MASTER: // also without arbitration, TODO!
sm_dma_start();
while (!sm_dma.state())
;
// a dma cycle into a device register may trigger an interrupt
// do not delete that condition
if (mailbox.arm2pru_req == arm2pru_req_cached)
mailbox.arm2pru_req = ARM2PRU_NONE; // clear request
break;
case ARM2PRU_DMA_ARB_CLIENT:
// start DMA cycle
// can not run parallel with INTR levels
sm_arb_start(ARBITRATION_PRIORITY_BIT_NP);
while (!sm_arb.state()) {
// sm_slave is most time critical, as it must keep track with MSYN/SSYN bus traffic.
// so give it more cpu cycles
while (!sm_slave.state())
;
// prohibited then.
sm_data_master_state = sm_data_master_state(); // execute only ONE state ,
// else DMA blocks will block prcoessing of other state machines
// throws signals to ARM, causes may issue mailbox.arm2pru_req
}
// process ARM commands in master and slave mode
// standard operation may be interrupt by other requests
if (arm2pru_req_cached = mailbox.arm2pru_req) {
// not ARM2PRU_NONE
switch (arm2pru_req_cached) {
case ARM2PRU_NOP: // needed to probe PRU run state
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_ARB_MODE_NONE: // ignore SACK condition
// from now on, ignore INTR requests and allow DMA request immediately
sm_arb_worker = &sm_arb_worker_none;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_ARB_MODE_CLIENT:
// request DMA from external Arbitrator
sm_arb_worker = &sm_arb_worker_client;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_ARB_MODE_MASTER:
sm_arb_worker = &sm_arb_worker_master;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_DMA:
// request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_*
sm_arb.request_mask |= PRIORITY_ARBITRATION_BIT_NP;
// sm_arb_worker() evaluates this,extern Arbitrator raises Grant, excution starts in future loop
// end of DMA is signaled to ARM with signal
/* TODO: speed up DMA
While DMA is active:
- SACK active: no GRANT forward necessary
no arbitration necessary
- INIT is monitored: no DC_LO/INIT monitoring necessary
- no scan for new ARM2PRU commands: ARM2PRU_DMA is blocking
- smaller chunks ?
*/
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_INTR:
// request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_*
// start one INTR cycle. May be raised in midst of slave cycle
// by ARM, if access to "active" register triggers INTR.
sm_arb.request_mask |= mailbox.intr.priority_arbitration_bit;
// sm_arb_worker() evaluates this, extern Arbitrator raises Grant,
// vector of GRANted level is transfered with statemachine sm_intr
// Atomically change state in a device's associates interrupt register.
// The Interupt Register is set immediately. No wait for INTR GRANT,
// INTR level may be blocked.
if (mailbox.intr.iopage_register_handle)
deviceregisters.registers[mailbox.intr.iopage_register_handle].value =
mailbox.intr.iopage_register_value;
mailbox.arm2pru_req = ARM2PRU_NONE; // done
// end of INTR is signaled to ARM with signal
break;
case ARM2PRU_INTR_CANCEL:
// cancels an INTR request. If already Granted, the GRANT is forwarded,
// and canceled by reaching a "SACK turnaround terminator" or "No SACK TIMEOUT" in the arbitrator.
sm_arb.request_mask &= ~ mailbox.intr.priority_arbitration_bit ;
// no completion event, could interfer with othe INTRs?
mailbox.arm2pru_req = ARM2PRU_NONE; // done
break ;
case ARM2PRU_INITPULSE:
if (!sm_init_state)
sm_init_state = (statemachine_state_func) &sm_init_start;
// INIT aborts DMA
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_POWERCYCLE:
sm_powercycle_state = (statemachine_state_func) &sm_powercycle_start;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_HALT:
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
__halt() ; // LA: trigger on timeout of REG_WRITE
break ;
}
// now SACK held and BBSY set, slave state machine ended, since BBSY found inactive
// debug pin reset by bus access
//DEBUG_PIN_SET(1) ;
sm_dma_start();
//DEBUG_PIN_SET(1) ;
while (!sm_dma.state())
//DEBUG_PIN_SET(1) ;
;// execute dma master cycles
// a dma cycle into a device register may trigger an interrupt
// do not delete that condition
if (mailbox.arm2pru_req == arm2pru_req_cached)
mailbox.arm2pru_req = ARM2PRU_NONE; // clear request
break;
case ARM2PRU_INTR:
// start one INTR cycle. May be raised in midst of slave cycle
// by ARM, if access to "active" register triggers INTR.
// no multiple levels simultaneously allowed, not parallel with DMA !
sm_arb_start(mailbox.intr.priority_bit);
// wait while INTR is accepted. This may take long time,
// if system is at high processor priority (PSW register)
while (!sm_arb.state()) {
// sm_slave is most time critical, as it must keep track with MSYN/SSYN bus traffic.
// so give it more cpu cycles
while (!sm_slave.state())
;
}
// now SACK held and BBSY set, slave state machine ended, since BBSY found inactive
sm_intr_start();
while (!sm_intr.state())
; // execute intr cycle as bus master
mailbox.arm2pru_req = ARM2PRU_NONE; // clear request
break;
case ARM2PRU_INITPULSE: // generate a pulse on UNIBUS INIT
// only busmaster may assert INIT. violated here!
sm_slave_start();
sm_init_start();
while (!sm_slave.state() || !sm_init.state())
;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_POWERCYCLE: // do ACLO/DCLO power cycle
// Runs for 4* POWERCYCLE_DELAY_MS millsecs, approx 1 sec.
// perform slave states in parallel, so emulated memory
// is existent for power fail trap and reboot
sm_slave_start();
sm_powercycle_start();
while (!sm_slave.state() || !sm_powercycle.state())
;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
default: // ignore all other requestes while executing emulation
;
} // switch
} // while (true)
}
}
// never reached
}
#pragma diag_pop

View File

@@ -1,43 +1,42 @@
/* pru1_pru_mailbox.c: datastructures common to PRU0 and PRU1
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
datastructures common to PRU0 and PRU1
Compiled in both PR0 and PRU1 code
To be included into both PRU0 and PRU1 projects.
Section ".pru_pru_mailbox_sec" to be placed at the end of PRU0 internal RAM.
pos in PRU0 space: 0x1f00
pos in PRU1 space: 0x3f00
See linker control file!
datastructures common to PRU0 and PRU1
Compiled in both PR0 and PRU1 code
To be included into both PRU0 and PRU1 projects.
Section ".pru_pru_mailbox_sec" to be placed at the end of PRU0 internal RAM.
pos in PRU0 space: 0x1f00
pos in PRU1 space: 0x3f00
See linker control file!
*/
#include <stdint.h>
#include "pru_pru_mailbox.h"
#define _PRU_PRU_MAILBOX_C_
// everything here is to be placed in the same section
#pragma DATA_SECTION(pru_pru_mailbox,".pru_pru_mailbox_sec")
volatile pru_pru_mailbox_t pru_pru_mailbox ;
volatile pru_pru_mailbox_t pru_pru_mailbox;

View File

@@ -1,51 +1,52 @@
/* pru1_statemachine_arbitration.c: state machine for INTR/DMA arbitration
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
Statemachine for execution of the Priority Arbitration protocol
NPR arbitration and BR interrupt arbitration
Statemachine for execution of the Priority Arbitration protocol
NPR arbitration and BR interrupt arbitration
UniBone is neither interrupt fielding processor nor abitrator.
It is a plain bus slave here.
PRU handles all 5 requests in parallel:
4x INTR BR4-BR7
1x DMA NPR.
Several ARM device may raise the same BR|NPR level, ARM must serialize this to PRU.
This state machine can handle ONLY ONE request at a time.
ARM code threads must wait until a request is completed by PRU.
Problem: Multiple devices may trigger DMA and Interrupts independently!
Solution: these events must be serialized by an "priority arbitration scheduler" ,
May be implemented by a event queue for each priority level,
containing refereneces to all devices requesting arbitration.
Flow:
1. ARM sets a REQUEST by
filling the RQUEST struct and perhaps DMA data
doing AMR2PRO_PRIORITY_ARBITRATION_REQUEST,
2. PRU sets BR4567|NPR lines according to open requests
3. PRU monitors IN GRANT lines BG4567,NPG.
IN state of idle requests is forwarded to BG|NPG OUT liens,
to be processed by other UNIBUS cards.
BG*|NPG IN state line of active request cleares BR*|NPR line,
sets SACK, and starts INTR or DMA state machine.
4. INTR or DMA sent a signal on compelte to PRU.
PRU may then start next request on same (completed) BR*|NPR level.
Or Todo: handle all 5 request/grants in parallel.
1. Start a request: set bit in _start()
2. PRU executes statemachine
3. on complete BBSY is acquires, UniBone is bus master
All references "PDP11BUS handbook 1979"
- At any time, CPU receives NPR it asserts NPG
- between CPU instructions:
All references "PDP11BUS handbook 1979"
- At any time, CPU receives NPR it asserts NPG
- between CPU instructions:
if PRI < n and BRn is received, assert BGn
else if PRI < 7 and BR7 is reived, assert BG7
else if PRI < 6 and BR6 is reived, assert BG6
@@ -53,7 +54,7 @@
else if PRI < 4 and BR4 is reived, assert BG4
if PRU detectes a BGINn which it not requested, it passes it to BGOUTn
If PRU detectes a BGINn which it not requested, it passes it to BGOUTn
"passing the grant"
if PRU detects BGIN which was requests, it "blocks the GRANT" )sets SACK and
transmit the INT (BG*) or becomes
@@ -67,6 +68,14 @@
BBSY is set before SACK is released. SACK is relased imemdiatley after BBSY,
enabling next arbitration in parallel to curretn data transfer
"Only the device with helds SACk asserted can assert BBSY
Several arbitration "workers" which set request, monitor or generate GRANT signals
and allocate SACK.
Which worker to use depends on wether a physical PDP-11 CPU is Arbitrator,
the Arbitrator is implmented here (CPU emulation),
or DMA should be possible always
(even if some other CPU monitr is holding SACK (11/34).
*/
#define _PRU1_STATEMACHINE_ARBITRATION_C_
@@ -78,150 +87,116 @@
#include "mailbox.h"
#include "pru1_buslatches.h"
#include "pru1_statemachine_arbitration.h"
#include "pru1_statemachine_arbitration.h"
statemachine_arbitration_t sm_arb;
// forwards ;
static uint8_t sm_arb_state_1(void);
static uint8_t sm_arb_state_2(void);
static uint8_t sm_arb_state_3(void);
static uint8_t sm_arb_state_4(void);
/********** NPR/NPG/SACK arbitrations **************/
void sm_arb_start(uint8_t priority_bit) {
sm_arb.priority_bit = priority_bit; // single priority bit for this arbitration process
sm_arb.state = &sm_arb_state_1;
// to be called on INIT signal: abort the abritration rpcoess
void sm_arb_reset() {
// cleanup: clear all REQUESTS and SACK
buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK | BIT(5), 0);
sm_arb.request_mask = 0;
sm_arb.bbsy_wait_grant_mask = 0;
}
// idle. call _start()
// execute in parallel with slave!
// pass BGIN[4-7],NPGIN to next device , if DMA engine idle
uint8_t sm_arb_state_idle() {
uint8_t tmpval;
tmpval = buslatches_getbyte(0);
// forward all 5 GRANT IN inverted to GRANT OUT
buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval)
;
return 1;
}
/* sm_arb_workers_*()
If return !=0: we have SACK on the GRANt lines return in a bit mask
see PRIORITY_ARBITRATION_BIT_*
*/
// wait for GRANT idle
// Assert REQUEST, wait for GRANT, assert SACK, wait for NPG==0, set SACK=0 ,
// execute in parallel with slave!
static uint8_t sm_arb_state_1() {
uint8_t tmpval;
tmpval = buslatches_getbyte(0);
// forward all lines, until idle
buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval) ;
// wait for GRANT idle, other cycle in progress?
if (tmpval & sm_arb.priority_bit)
return 0;
// no need to wait for SACK: arbitrator responds only with a GRANT IN
buslatches_setbits(1, sm_arb.priority_bit, sm_arb.priority_bit); // REQUEST = latch1
sm_arb.state = &sm_arb_state_2; // wait for GRANT IN active
return 0;
}
/* worker_none():
* No arbitration protocol. Make DMA possible in every bus configuration:
* Ignores active SACK and/or BBSY from other bus masters.
* For diagnostics on hung CPU, active device or console processor holding SACK.
*/
uint8_t sm_arb_worker_none() {
// Unconditionally forward GRANT IN to GRANT OUT
uint8_t grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN
buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK, ~grant_mask) ;
// wait for BG*,NPG or INIT
// execute in parallel with slave!
static uint8_t sm_arb_state_2() {
uint8_t tmpval;
if (buslatches_getbyte(7) & BIT(3)) { // INIT stops transaction: latch[7], bit 3
// cleanup: clear all REQUESTS and SACK
buslatches_setbits(1, ARBITRATION_PRIORITY_MASK| BIT(5), 0);
// Todo: signal INIT to ARM!
sm_arb.state = &sm_arb_state_idle;
return 0 ;
}
tmpval = buslatches_getbyte(0);
// forward all other BG lines
// preceding arbitration must see BG removed by master on SACK
if (tmpval & sm_arb.priority_bit) {
// got GRANT IN. Clear GRANT OUT, don't pass to next device
tmpval &= ~sm_arb.priority_bit ;
buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval); // forward all without our GRANT
// set SACK
buslatches_setbits(1, BIT(5), BIT(5));
sm_arb.state = &sm_arb_state_3;
// ignore BR* INTR requests, only ack DMA.
if (sm_arb.request_mask & PRIORITY_ARBITRATION_BIT_NP) {
sm_arb.request_mask &= ~PRIORITY_ARBITRATION_BIT_NP;
return PRIORITY_ARBITRATION_BIT_NP;
} else
buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval); // forward all
return 0;
}
/* worker_client:
Issue request to extern Arbitrator (PDP-11 CPU).
Watch for GRANTs on the bus signal lines, then raise SACK.
Wait for current bus master to release bus => Wait for BBSY clear.
Then return GRANTed request.
"Wait for BBSY clear" may not be part of the arbitration protocol.
But it guarantees caller may now issue an DMA or INTR.
*/
uint8_t sm_arb_worker_client() {
uint8_t grant_mask;
// Always update UNIBUS BR/NPR lines, are ORed with requests from other devices.
buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK, sm_arb.request_mask)
;
// read GRANT IN lines from CPU (Arbitrator). Only one at a time may be active
// Arbitrator asserts SACK is inactive
// latch[0]: BG signals are inverted, "get" is non-inverting: bit = active signal.
// "set" is inverting!
grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN
// forward un-requested GRANT IN to GRANT OUT for other cards on neighbor slots
buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK & ~sm_arb.request_mask, ~grant_mask)
;
if (sm_arb.bbsy_wait_grant_mask == 0) {
// State 1: Wait For GRANT:
// process the requested grant for an open requests.
grant_mask &= sm_arb.request_mask;
if (grant_mask) {
// one of our requests was granted:
// set SACK
// AND simultaneously Clear granted requests BR*/NPR
// BIT(5): SACK mask and level
buslatches_setbits(1, (PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask) | BIT(5),
~grant_mask | BIT(5))
;
/*
buslatches_setbits(1, BIT(5), BIT(5));
// Arbitrator now clears GRANT IN
// clear granted requests BR* / NPR on UNIBUS
// "~": disable BR for set BG
buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask, ~grant_mask)
;
*/
// clear granted requests internally
sm_arb.request_mask &= ~grant_mask;
// Arbitrator should remove GRANT now. Data section on Bus still BBSY
sm_arb.bbsy_wait_grant_mask = grant_mask; // next is State 2: wait for BBSY clear
}
return 0; // no GRANT for us, or wait for BBSY
} else {
// wait for BBSY to clear
if (buslatches_getbyte(1) & BIT(6))
return 0; // BBSY still set
grant_mask = sm_arb.bbsy_wait_grant_mask;
sm_arb.bbsy_wait_grant_mask = 0; // Next State is 1
return grant_mask; // signal what request we got granted.
}
// UNIBUS DATA section is indepedent: MSYN, SSYN, BBSY may still be active.
// -> DMA and INTR statemachine must wait for BBSY.
}
/* "worker_master"
Act as Arbitrator.
Grant highest of requests, if SACK deasserted.
Execute UNIBUS priority algorithm:
- Grant DMA request, if present
- GRANT BR* in descending priority, when CPU execution level allows .
*/
uint8_t sm_arb_worker_master() {
return 0;
}
// GRANT received. wait for previous bus master to complete transaction,
// then become bus master
// Forwarding of other GRANTs not necessary ... arbitrator granted us.
static uint8_t sm_arb_state_3() {
if (buslatches_getbyte(7) & BIT(3)) { // INIT stops transaction: latch[7], bit 3
// cleanup: clear all REQUESTS and SACk
buslatches_setbits(1, ARBITRATION_PRIORITY_MASK| BIT(5), 0);
// Todo: signal INIT to ARM!
sm_arb.state = &sm_arb_state_idle;
return 1;
}
if (buslatches_getbyte(0) & sm_arb.priority_bit) // wait for GRANT IN to be deasserted
return 0;
// wait until old bus master cleared BBSY
if (buslatches_getbyte(1) & BIT(6))
return 0;
// wait until SSYN deasserted by old slave
if (buslatches_getbyte(4) & BIT(5))
return 0;
// now become new bus master: Set BBSY, Clear REQUEST
// BBSY= bit 6
buslatches_setbits(1, sm_arb.priority_bit | BIT(6), BIT(6));
// SACK is cleared later in "data transfer" statemachines (DMA or INTR)
sm_arb.state = &sm_arb_state_4; // bus mastership acquired
return 1;
}
// bus mastership acquired. DMA data transfer must terminate this state
static uint8_t sm_arb_state_4() {
return 1;
}
#ifdef USED
S1:
if SACK deasserted:
set all Reqest lines in latch 1, which have bits set in mailbox.arb_request
(requests all in parallel
wait for any GRANT
// always: pass un-requestedgrants
read GRANT lines BG*IN, NPGIN
mask with my requests
write pattern to BG*OUT, NPGOUT
S2:
get all 5 GRANT signals
get mask with highest asserted GRANT line ("active Grant") , which was requested in S1
// always: pass un-requestedgrants to BG*OUT, NPGOUT
if grants for us:
set SACK
wait for active GRANT line going LOW
wait until BBSY==0 && SSYN==0 && active GRANT==0 free (long time!)
set BBSY
set SACK low
NO: SHOULD BE "BEFORE LAST DATA TRAMSFER BY CURRENT MASTER"
-> interaction with statemachine_master
setting SACK low early start arbitration early.
High priority interrupts while bus occpied by DMA (after ealry priority cycle)
are ignoreed then.
set bit in mailbox in .arb_grant
INIT: clear all requests, set INIT flag in mailbox!
freeing BBSY:
on end of next DMA trasnfer,
OR after INTR transmission
#endif

View File

@@ -1,47 +1,68 @@
/* pru1_statemachine_arbitration.h: state machine for INTR/DMA arbitration
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_ARBITRATION_H_
#define _PRU1_STATEMACHINE_ARBITRATION_H_
#include <stdint.h>
// execution of a state. return : 1, if statemachine stopped
typedef uint8_t (*sm_arb_state_func_ptr)(void);
// a priority-arbitration-worker returns a bit mask with the GRANT signal he recognized
typedef uint8_t (*statemachine_arb_worker_func)();
typedef struct {
sm_arb_state_func_ptr state; // current state as ptr to "state function"
uint8_t priority_bit ; // single priority bit for this arbitration process
// one of ARBITRATION_PRIORITY_*
// There are 5 request/grant signals (BR4,5,6,7 and NPR).
// These are encoded as bitmask fitting the buslatch[0] or[1]
// BR/NPR lines = set of _PRIORITY_ARBITRATION_BIT_*
uint8_t request_mask;
// sm_arb has 2 states: State 1 "Wait for GRANT" and State 2 "wait for BBSY"
// When arbitrator GRANts a request, we set SACK, GRAMT is cleared and we wait
// for BBSY clear.
// 0: not waitong for BBSY.
// != saves GRANTed reqiest and idnicates BBSY wait state
uint8_t bbsy_wait_grant_mask ;
} statemachine_arbitration_t;
/* receives a grant_mask with 1 bit set and returns the index of that bit
when interpreted as intr odma request
BR4->0, BR5->1,BR6->2, BR7->3,NPR->4
grant_mask as value from buslatch 0: BR4 at bit 0
Undefined result if grant_mask empty or > 0x10
*/
#define PRIORITY_ARBITRATION_INTR_BIT2IDX(grant_mask) \
__lmbd((grant_mask), 1)
// "LMBD" = "Left Most Bit Detect"
#ifndef _PRU1_STATEMACHINE_ARBITRATION_C_
extern statemachine_arbitration_t sm_arb;
#endif
void sm_arb_start(uint8_t priority_bit);
uint8_t sm_arb_state_idle(void) ;
void sm_arb_reset(void);
uint8_t sm_arb_worker_none(void);
uint8_t sm_arb_worker_client(void);
uint8_t sm_arb_worker_master(void);
#endif

View File

@@ -1,56 +1,58 @@
/* pru1_statemachine_dma.c: state machine for bus master DMA
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
Statemachines to execute multiple masterr DATO or DATI cycles.
All references "PDP11BUS handbook 1979"
Precondition: BBSY already asserted (arbitration got)
Statemachines to execute multiple masterr DATO or DATI cycles.
All references "PDP11BUS handbook 1979"
Precondition: BBSY already asserted (arbitration got)
Master reponds to INIT by stopping transactions.
new state
Master reponds to INIT by stopping transactions.
new state
Start: setup dma mailbox setup with
startaddr, wordcount, cycle, words[]
Then sm_dma_init() ;
sm_dma_state = DMA_STATE_RUNNING ;
while(sm_dma_state != DMA_STATE_READY)
sm_dma_service() ;
state is 0 for OK, or 2 for timeout error.
mailbox.dma.cur_addr is error location
Start: setup dma mailbox setup with
startaddr, wordcount, cycle, words[]
Then sm_dma_init() ;
sm_dma_state = DMA_STATE_RUNNING ;
while(sm_dma_state != DMA_STATE_READY)
sm_dma_service() ;
state is 0 for OK, or 2 for timeout error.
mailbox.dma.cur_addr is error location
Speed: (clpru 2.2, -O3:
Example: DATI, time SSYN- active -> (processing) -> MSYN inactive
a) 2 states, buslatch_set/get function calls, TIMEOUT_SET/REACHED(75) -> 700ns
b) 2 states, buslatch_set/get macro, TIMEOUT_SET/REACHED(75) -> 605ns
c) 2 states, no TIMEOUT (75 already met) -> 430ns
d) 1 marged state, no TIMEOUT ca. 350ns
Speed: (clpru 2.2, -O3:
Example: DATI, time SSYN- active -> (processing) -> MSYN inactive
a) 2 states, buslatch_set/get function calls, TIMEOUT_SET/REACHED(75) -> 700ns
b) 2 states, buslatch_set/get macro, TIMEOUT_SET/REACHED(75) -> 605ns
c) 2 states, no TIMEOUT (75 already met) -> 430ns
d) 1 marged state, no TIMEOUT ca. 350ns
! Uses single global timeout, don't run in parallel with other statemachines using timeout !
! Uses single global timeout, don't run in parallel with other statemachines using timeout !
*/
#define _PRU1_STATEMACHINE_DMA_C_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
@@ -58,6 +60,7 @@
#include "mailbox.h"
#include "pru1_buslatches.h"
#include "pru1_utils.h"
#include "pru1_timeouts.h"
#include "pru1_statemachine_arbitration.h"
#include "pru1_statemachine_dma.h"
@@ -65,38 +68,52 @@
/* sometimes short timeout of 75 and 150ns are required
* 75ns between state changes is not necessary, code runs longer
* 150ns between state changes is necessary
* Overhead for extra state and TIEOUTSET/REACHED is 100ns
* Overhead for extra state and TIMEOUTSET/REACHED is 100ns
*/
statemachine_dma_t sm_dma;
/********** Master DATA cycles **************/
// forwards ;
static uint8_t sm_dma_state_1(void);
static uint8_t sm_dma_state_11(void);
static uint8_t sm_dma_state_21(void);
static uint8_t sm_dma_state_99(void);
static statemachine_state_func sm_dma_state_1(void);
static statemachine_state_func sm_dma_state_11(void);
static statemachine_state_func sm_dma_state_21(void);
static statemachine_state_func sm_dma_state_99(void);
// dma mailbox setup with
// startaddr, wordcount, cycle, words[] ?
// "cycle" must be UNIBUS_CONTROL_DATI or UNIBUS_CONTROL_DATO
// BBSY already set, SACK held asserted
void sm_dma_start() {
// Wait for BBSY, SACK already held asserted
statemachine_state_func sm_dma_start() {
// assert BBSY: latch[1], bit 6
// buslatches_setbits(1, BIT(6), BIT(6));
mailbox.dma.cur_addr = mailbox.dma.startaddr;
sm_dma.dataptr = (uint16_t *) mailbox.dma.words; // point to start of data buffer
sm_dma.state = &sm_dma_state_1;
sm_dma.cur_wordsleft = mailbox.dma.wordcount;
mailbox.dma.cur_status = DMA_STATE_RUNNING;
// do not wait for BBSY here. This is part of Arbitration.
buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY
// next call to sm_dma.state() starts state machine
return (statemachine_state_func) &sm_dma_state_1;
}
/*
// wait for BBSY deasserted, then assert
static statemachine_state_func sm_dma_state_1() {
if (buslatches_getbyte(1) & BIT(6))
return (statemachine_state_func) &sm_dma_state_1; // wait
buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY
return (statemachine_state_func) &sm_dma_state_1;
}
*/
// place address and control bits onto bus, also data for DATO
// If slave address is internal (= implemented by UniBone),
// fast UNIBUS slave protocol is generated on the bus.
static uint8_t sm_dma_state_1() {
static statemachine_state_func sm_dma_state_1() {
uint32_t tmpval;
uint32_t addr = mailbox.dma.cur_addr; // non-volatile snapshot
uint16_t data;
@@ -104,11 +121,10 @@ static uint8_t sm_dma_state_1() {
// uint8_t page_table_entry;
uint8_t b;
bool internal;
// should test SACK and BBSY !
if (mailbox.dma.cur_status != DMA_STATE_RUNNING || mailbox.dma.wordcount == 0)
return 1; // still stopped
// BBSY released
if (mailbox.dma.cur_status != DMA_STATE_RUNNING || mailbox.dma.wordcount == 0)
return NULL; // still stopped
if (sm_dma.cur_wordsleft == 1) {
// deassert SACK, enable next arbitration cycle
@@ -173,17 +189,18 @@ static uint8_t sm_dma_state_1() {
buslatches_setbits(4, BIT(4), 0); // master deassert MSYN
buslatches_setbyte(5, 0); // master removes data
buslatches_setbyte(6, 0);
// perhaps PRU2ARM_INTERRUPT now active,
// assert SSYN after ARM completes "active" register logic and INIT
while (mailbox.events.eventmask) ;
// perhaps ARM issued ARM2PRU_INTR, request set in parallel state machine.
// Arbitrator will GRANT it after DMA ready (SACK deasserted).
// assert SSYN after ARM completes "active" register logic
// while (mailbox.events.event_deviceregister) ;
buslatches_setbits(4, BIT(5), 0); // slave deassert SSYN
sm_dma.state = &sm_dma_state_99; // next word
return (statemachine_state_func) &sm_dma_state_99; // next word
} else {
// DATO to external slave
// wait for a slave SSYN
TIMEOUT_SET(NANOSECS(1000*UNIBUS_TIMEOUT_PERIOD_US));
sm_dma.state = &sm_dma_state_21; // wait SSYN DATAO
timeout_set(TIMEOUT_DMA, MICROSECS(UNIBUS_TIMEOUT_PERIOD_US));
return (statemachine_state_func) &sm_dma_state_21; // wait SSYN DATAO
}
} else {
// DATI or DATIP
@@ -219,30 +236,29 @@ static uint8_t sm_dma_state_1() {
buslatches_setbits(4, BIT(4), 0); // master deassert MSYN
buslatches_setbyte(5, 0); // slave removes data
buslatches_setbyte(6, 0);
// perhaps PRU2ARM_INTERRUPT now active,
// assert SSYN after ARM completes "active" register logic and INIT
while (mailbox.events.eventmask) ;
// perhaps ARM issued ARM2PRU_INTR, request set in parallel state machine.
// Arbitrator will GRANT it after DMA ready (SACK deasserted).
// assert SSYN after ARM completes "active" register logic
// while (mailbox.events.event_deviceregister) ;
buslatches_setbits(4, BIT(5), 0); // slave deassert SSYN
sm_dma.state = &sm_dma_state_99; // next word
return (statemachine_state_func) &sm_dma_state_99; // next word
} else {
// DATI to external slave
// wait for a slave SSYN
TIMEOUT_SET(NANOSECS(1000*UNIBUS_TIMEOUT_PERIOD_US));
sm_dma.state = &sm_dma_state_11; // wait SSYN DATI
timeout_set(TIMEOUT_DMA, MICROSECS(UNIBUS_TIMEOUT_PERIOD_US));
return (statemachine_state_func) &sm_dma_state_11; // wait SSYN DATI
}
}
return 0; // still running
}
// DATI to external slave: MSYN set, wait for SSYN or timeout
static uint8_t sm_dma_state_11() {
static statemachine_state_func sm_dma_state_11() {
uint16_t tmpval;
sm_dma.state_timeout = TIMEOUT_REACHED;
sm_dma.state_timeout = timeout_reached(TIMEOUT_DMA);
// SSYN = latch[4], bit 5
if (!sm_dma.state_timeout && !(buslatches_getbyte(4) & BIT(5)))
return 0; // no SSYN yet: wait
return (statemachine_state_func) &sm_dma_state_11; // no SSYN yet: wait
// SSYN set by slave (or timeout). read data
__delay_cycles(NANOSECS(75) - 6); // assume 2*3 cycles for buslatches_getbyte
@@ -257,16 +273,15 @@ static uint8_t sm_dma_state_11() {
buslatches_setbits(4, BIT(4), 0);
// DATI: remove address,control, MSYN,SSYN from bus, 75ns after MSYN inactive
__delay_cycles(NANOSECS(75) - 8); // assume 8 cycles for state change
sm_dma.state = &sm_dma_state_99;
return 0; // still running
return (statemachine_state_func) &sm_dma_state_99;
}
// DATO to external slave: wait for SSYN or timeout
static uint8_t sm_dma_state_21() {
sm_dma.state_timeout = TIMEOUT_REACHED; // SSYN timeout?
static statemachine_state_func sm_dma_state_21() {
sm_dma.state_timeout = timeout_reached(TIMEOUT_DMA); // SSYN timeout?
// SSYN = latch[4], bit 5
if (!sm_dma.state_timeout && !(buslatches_getbyte(4) & BIT(5)))
return 0; // no SSYN yet: wait
return (statemachine_state_func) &sm_dma_state_21; // no SSYN yet: wait
// SSYN set by slave (or timeout): negate MSYN, remove DATA from bus
buslatches_setbits(4, BIT(4), 0); // deassert MSYN
@@ -274,12 +289,11 @@ static uint8_t sm_dma_state_21() {
buslatches_setbyte(6, 0);
// DATO: remove address,control, MSYN,SSYN from bus, 75ns after MSYN inactive
__delay_cycles(NANOSECS(75) - 8); // assume 8 cycles for state change
sm_dma.state = &sm_dma_state_99;
return 0; // still running
return (statemachine_state_func) &sm_dma_state_99;
}
// word is transfered, or timeout.
static uint8_t sm_dma_state_99() {
static statemachine_state_func sm_dma_state_99() {
uint8_t final_dma_state;
// from state_12, state_21
@@ -302,14 +316,12 @@ static uint8_t sm_dma_state_99() {
buslatches_setbits(1, BIT(5), 0); // deassert SACK = latch[1], bit 5
} else
final_dma_state = DMA_STATE_RUNNING; // more words: continue
}
sm_dma.state = &sm_dma_state_1; // in any case, reloop
if (final_dma_state == DMA_STATE_RUNNING) {
// dataptr and wordsleft already incremented
// dataptr and words_left already incremented
mailbox.dma.cur_addr += 2; // signal progress to ARM
return 0;
return (statemachine_state_func) &sm_dma_state_1; // reloop
} else {
// remove addr and control from bus
buslatches_setbyte(2, 0);
@@ -317,10 +329,20 @@ static uint8_t sm_dma_state_99() {
buslatches_setbits(4, 0x3f, 0);
// remove BBSY: latch[1], bit 6
buslatches_setbits(1, BIT(6), 0);
// terminate arbitration state
sm_arb.state = &sm_arb_state_idle;
// SACK already de-asserted at wordcount==1
timeout_cleanup(TIMEOUT_DMA);
mailbox.dma.cur_status = final_dma_state; // signal to ARM
return 1; // now stopped
timeout_cleanup(TIMEOUT_DMA);
// signal to ARM
mailbox.events.event_dma = 1;
// ARM is clearing this, before requesting new DMA.
// no concurrent ARP+PRU access
PRU2ARM_INTERRUPT
;
return NULL; // now stopped
}
}

View File

@@ -1,53 +1,46 @@
/* pru1_statemachine_dma.h: state machine for bus master DMA
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_DMA_H_
#define _PRU1_STATEMACHINE_DMA_H_
// execution of a state. return : 1, if statemachine stopped
typedef uint8_t (*sm_dma_state_func_ptr)(void);
#include "pru1_utils.h" // statemachine_state_func
// Transfers a block of worst as data cycles
typedef struct {
sm_dma_state_func_ptr state; // current state as ptr to "state fucntion"
uint8_t state_timeout; // timeout occured?
uint16_t *dataptr ; // points to current word in mailbox.words[] ;
uint16_t *dataptr; // points to current word in mailbox.words[] ;
uint16_t cur_wordsleft; // # of words left to transfer
} statemachine_dma_t;
#ifndef _PRU1_STATEMACHINE_DMA_C_
extern statemachine_dma_t sm_dma;
#endif
void sm_dma_start(void) ;
statemachine_state_func sm_dma_start(void);
#endif

View File

@@ -1,42 +1,45 @@
/* pru1_statemachine_init.c: state machine for pulse on INIT
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
Statemachine for a pulse on UNIBUS INIT
! Uses single global timeout, don't run in parallel with other statemachines using timeout !
Statemachine for a pulse on UNIBUS INIT
! Uses single global timeout, don't run in parallel with other statemachines using timeout !
*/
#define _PRU1_STATEMACHINE_INIT_C_
#include <stdlib.h>
#include <stdint.h>
#include "mailbox.h"
#include "pru1_utils.h"
#include "pru1_timeouts.h"
#include "pru1_buslatches.h"
#include "pru1_statemachine_init.h"
#include "pru1_statemachine_arbitration.h"
/*** detection of changes in INIT,DCLO,ACLO ***/
@@ -44,48 +47,54 @@
// history initialized (among others) by powercycle
// Assume this events come so slow, no one gets raised until
// prev event processed.
void do_event_initializationsignals() {
uint8_t tmp = buslatches_getbyte(7) & 0x38 ;
if (tmp != mailbox.events.initialization_signals_cur) {
// save old state, so ARM can detect what changed
mailbox.events.initialization_signals_prev = mailbox.events.initialization_signals_cur ;
mailbox.events.initialization_signals_cur = tmp ;
mailbox.events.eventmask |= EVENT_INITIALIZATIONSIGNALS ;
PRU2ARM_INTERRUPT ;
}
void do_event_initializationsignals() {
uint8_t mb_cur = mailbox.events.initialization_signals_cur; // as saved
uint8_t bus_cur = buslatches_getbyte(7) & 0x38; // now sampled
if (bus_cur & INITIALIZATIONSIGNAL_INIT)
sm_arb.request_mask = 0 ; // INIT clears all PRIORITY request signals
// SACK cleared later on end of INTR/DMA transaction
if (bus_cur != mb_cur) {
// save old state, so ARM can detect what changed
mailbox.events.initialization_signals_prev = mb_cur;
mailbox.events.initialization_signals_cur = bus_cur;
// trigger the correct event: power and/or INIT
if ((mb_cur ^ bus_cur) & (INITIALIZATIONSIGNAL_DCLO | INITIALIZATIONSIGNAL_ACLO))
// AC_LO or DC_LO changed
mailbox.events.event_power = 1;
if ((mb_cur ^ bus_cur) & INITIALIZATIONSIGNAL_INIT)
// INIT changed
mailbox.events.event_init = 1;
PRU2ARM_INTERRUPT
;
}
// No event queue: INIT event may overide register access ... thats OK:
// INIT reset all register states
statemachine_init_t sm_init;
}
// forwards
uint8_t sm_init_state_idle(void);
static uint8_t sm_init_state_1(void);
static statemachine_state_func sm_init_state_1(void);
// setup
void sm_init_start() {
TIMEOUT_SET(MILLISECS(INITPULSE_DELAY_MS))
;
statemachine_state_func sm_init_start() {
timeout_set(TIMEOUT_INIT, MILLISECS(INITPULSE_DELAY_MS));
// INIT: latch[7], bit 3
buslatches_setbits(7, BIT(3), BIT(3)); // assert INIT
mailbox.events.initialization_signals_prev &= ~INITIALIZATIONSIGNAL_INIT ; // force INIT event
do_event_initializationsignals() ;
sm_init.state = &sm_init_state_1;
mailbox.events.initialization_signals_prev &= ~INITIALIZATIONSIGNAL_INIT; // force INIT event
return (statemachine_state_func) &sm_init_state_1;
}
uint8_t sm_init_state_idle() {
return 1; // ready
}
static uint8_t sm_init_state_1() {
if (!TIMEOUT_REACHED)
return 0;
static statemachine_state_func sm_init_state_1() {
if (!timeout_reached(TIMEOUT_INIT))
return (statemachine_state_func) &sm_init_state_1; // wait
buslatches_setbits(7, BIT(3), 0); // deassert INIT
do_event_initializationsignals() ;
sm_init.state = &sm_init_state_idle;
return 1;
timeout_cleanup(TIMEOUT_INIT);
return NULL; // ready
}
// terminate INIT
// To be called from Powercycle?
void sm_init_reset() {
buslatches_setbits(7, BIT(3), 0); // deassert INIT
do_event_initializationsignals();
timeout_cleanup(TIMEOUT_INIT);
}

View File

@@ -1,51 +1,45 @@
/* pru1_statemachine_init.h: state machine for pulse on INIT
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_INIT_H_
#define _PRU1_STATEMACHINE_INIT_H_
#include <stdint.h>
#include "pru1_utils.h" // statemachine_state_func
#define INITPULSE_DELAY_MS 250 // length of INIT pulse
// execution of a state. return : 1, if statemachine stopped
typedef uint8_t (*sm_powercycle_init_func_ptr)(void);
typedef struct {
sm_powercycle_init_func_ptr state; // current state as ptr to "state function"
} statemachine_init_t;
#ifndef _PRU1_STATEMACHINE_INIT_C_
extern uint8_t prev_initialization_signals ;
extern statemachine_init_t sm_init;
extern uint8_t prev_initialization_signals;
#endif
void do_event_initializationsignals(void) ;
void do_event_initializationsignals(void);
void sm_init_start();
void sm_init_reset(void);
statemachine_state_func sm_init_start();
#endif

View File

@@ -1,35 +1,37 @@
/* pru1_statemachine_intr.c: state machine to transfer an interrupt vector
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
State machines to transfer an interrupt vector.
All references "PDP11BUS handbook 1979"
Precondition: BBSY already asserted (arbitration got)
State machines to transfer an interrupt vector.
All references "PDP11BUS handbook 1979"
Precondition: BBSY already asserted (arbitration got)
*/
#define _PRU1_STATEMACHINE_INTR_C_
#include <stdlib.h>
#include <stdint.h>
//#include "devices.h"
@@ -37,34 +39,30 @@
#include "pru1_buslatches.h"
#include "pru1_utils.h"
#include "pru1_statemachine_arbitration.h"
//#include "pru1_statemachine_arbitration.h"
#include "pru1_statemachine_intr.h"
// states
statemachine_intr_t sm_intr;
// forwards
static uint8_t sm_intr_state_idle(void);
static uint8_t sm_intr_state_1(void);
static uint8_t sm_intr_state_2(void);
static statemachine_state_func sm_intr_state_1(void);
static statemachine_state_func sm_intr_state_2(void);
// BBSY already set, SACK held asserted
void sm_intr_start() {
// BBSY already asserted. : latch[1], bit 6
// buslatches_setbits(1, BIT(6), BIT(6));
sm_intr.state = &sm_intr_state_1;
// next call to sm_intr.state() starts state machine
}
static uint8_t sm_intr_state_idle() {
return 1;
// Wait for BBSY deasserted, then assert, SACK already held asserted
statemachine_state_func sm_intr_start() {
// Do not wait for BBSY here, this is part of Arbitration
// if (buslatches_getbyte(1) & BIT(6))
// return (statemachine_state_func) &sm_intr_start; // wait
buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY
return (statemachine_state_func) &sm_intr_state_1;
}
// place vector onto data, then set INTR
static uint8_t sm_intr_state_1() {
uint16_t vector = mailbox.intr.vector;
static statemachine_state_func sm_intr_state_1() {
buslatches_setbyte(5, vector & 0xff); // DATA[0..7] = latch[5]
buslatches_setbyte(6, vector >> 8); // DATA[8..15] = latch[6]
buslatches_setbyte(5, sm_intr.vector & 0xff); // DATA[0..7] = latch[5]
buslatches_setbyte(6, sm_intr.vector >> 8); // DATA[8..15] = latch[6]
// set INTR
buslatches_setbits(7, BIT(0), BIT(0)); // INTR = latch 7, bit 0
@@ -75,25 +73,42 @@ static uint8_t sm_intr_state_1() {
buslatches_setbits(1, BIT(5), 0); // SACK = latch[1], bit 5
// wait for processor to accept vector (no timeout?)
sm_intr.state = &sm_intr_state_2;
return 0;
return (statemachine_state_func) &sm_intr_state_2;
}
// wait for SSYN
static uint8_t sm_intr_state_2() {
static statemachine_state_func sm_intr_state_2() {
if (!(buslatches_getbyte(4) & BIT(5)))
return 0;
return (statemachine_state_func) &sm_intr_state_2; // wait
// received SSYN
//remove vector, then remove INTR
// Complete and signal this INTR transaction only after ARM has processed the previous event.
// INTR may come faster than ARM Linux can process,
// especially if Arbitrator grants INTRs of multiple levels almost simultaneaously in parallel.
if (mailbox.events.event_intr)
return (statemachine_state_func) &sm_intr_state_2; // wait
// remove vector
buslatches_setbyte(5, 0); // DATA[0..7] = latch[5]
buslatches_setbyte(6, 0); // DATA[8..15] = latch[6]
// deassert INTR. Interrupt fielding processor then removes SSYN
buslatches_setbits(7, BIT(0), 0); // INTR = latch 7, bit 0
// deassert BBSY
buslatches_setbits(1, BIT(6), 0);
// SACK already removed
sm_intr.state = &sm_intr_state_idle;
return 1;
// signal to ARM which INTR was completed
// change mailbox only after ARM has ack'ed mailbox.events.event_intr
mailbox.events.event_intr_level_index = sm_intr.level_index;
mailbox.events.event_intr = 1;
// ARM is clearing this, before requesting new interrupt of same level
// so no concurrent ARP+PRU access
PRU2ARM_INTERRUPT
;
return NULL; // ready
// master still drives SSYN
}

View File

@@ -1,43 +1,41 @@
/* pru1_statemachine_intr.h: state machine to transfer an interrupt vector
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_INTR_H_
#define _PRU1_STATEMACHINE_INTR_H_
// execution of a state. return : 1, if statemachine stopped
typedef uint8_t (*sm_intr_state_func_ptr)(void);
#include "pru1_utils.h" // statemachine_state_func
// Transfers a block of worst as data cycles
typedef struct {
sm_intr_state_func_ptr state; // current state as ptr to "state fucntion"
uint16_t vector; // interrupt vector to transfer
uint8_t level_index; // 0..3 = BR..BR7. to be returned to ARM on complete
} statemachine_intr_t;
#ifndef _PRU1_STATEMACHINE_INTR_C_
extern statemachine_dma_t sm_intr;
#endif
extern statemachine_intr_t sm_intr;
void sm_intr_start(void);
statemachine_state_func sm_intr_start(void);
#endif

View File

@@ -1,27 +1,28 @@
/* pru1_statemachine_powercycle.c: state machine to generate an ACLO/DCLO pseudo power cycle
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
Statemachine for execution of an ACLO/DCLO pseudo power cycle.
@@ -41,81 +42,68 @@
#define _PRU1_STATEMACHINE_POWERCYCLE_C_
#include <stdlib.h>
#include <stdint.h>
#include "mailbox.h"
#include "pru1_utils.h"
#include "pru1_timeouts.h"
#include "pru1_buslatches.h"
#include "pru1_statemachine_init.h"
#include "pru1_statemachine_powercycle.h"
statemachine_powercycle_t sm_powercycle;
// forwards ; /
uint8_t sm_powercycle_state_idle(void);
static uint8_t sm_powercycle_state_1(void);
static uint8_t sm_powercycle_state_2(void);
static uint8_t sm_powercycle_state_3(void);
static uint8_t sm_powercycle_state_4(void);
static statemachine_state_func sm_powercycle_state_1(void);
static statemachine_state_func sm_powercycle_state_2(void);
static statemachine_state_func sm_powercycle_state_3(void);
static statemachine_state_func sm_powercycle_state_4(void);
// setup with
void sm_powercycle_start() {
sm_powercycle.state = &sm_powercycle_state_1;
statemachine_state_func sm_powercycle_start() {
return (statemachine_state_func) &sm_powercycle_state_1;
// next call to sm_slave.state() starts state machine
}
uint8_t sm_powercycle_state_idle() {
return 1; // ready
}
// "Line power shutdown": assert ACLO, then wait
static uint8_t sm_powercycle_state_1() {
static statemachine_state_func sm_powercycle_state_1() {
buslatches_setbits(7, BIT(4), BIT(4)); // ACLO asserted
TIMEOUT_SET(MILLISECS(POWERCYCLE_DELAY_MS))
; // wait for DC power shutdown
sm_powercycle.state = &sm_powercycle_state_2;
timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // wait for DC power shutdown
// DEBUG_OUT(0x01) ;
do_event_initializationsignals() ;
// DEBUG_OUT(0x02) ;
return 0;
return (statemachine_state_func) &sm_powercycle_state_2;
}
// "Power supply switched off": assert DCLO, then wait
static uint8_t sm_powercycle_state_2() {
if (!TIMEOUT_REACHED)
return 0;
static statemachine_state_func sm_powercycle_state_2() {
if (!timeout_reached(TIMEOUT_POWERCYCLE))
return (statemachine_state_func) &sm_powercycle_state_2; // wait
buslatches_setbits(7, BIT(5), BIT(5)); // DCLO asserted
TIMEOUT_SET(MILLISECS(POWERCYCLE_DELAY_MS))
; // system powered off
sm_powercycle.state = &sm_powercycle_state_3;
// DEBUG_OUT(0x03) ;
do_event_initializationsignals() ;
// DEBUG_OUT(0x04) ;
return 0;
timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // system powered off
// DEBUG_OUT(0x03) ;
// DEBUG_OUT(0x04) ;
// make sure we don't "come up from power" with INIT already set.
sm_init_reset();
return (statemachine_state_func) &sm_powercycle_state_3;
}
// "Line power back again": deassert ACLO, then wait
static uint8_t sm_powercycle_state_3() {
if (!TIMEOUT_REACHED)
return 0;
static statemachine_state_func sm_powercycle_state_3() {
if (!timeout_reached(TIMEOUT_POWERCYCLE))
return (statemachine_state_func) &sm_powercycle_state_3; // wait
buslatches_setbits(7, BIT(4), 0); // ACLO deasserted
TIMEOUT_SET(MILLISECS(POWERCYCLE_DELAY_MS))
; // "power supply stabilizing"
sm_powercycle.state = &sm_powercycle_state_4;
do_event_initializationsignals() ;
return 0;
timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // "power supply stabilizing"
return (statemachine_state_func) &sm_powercycle_state_4;
}
// "Logic power stabilized": deassert DCLO, ready
static uint8_t sm_powercycle_state_4() {
if (!TIMEOUT_REACHED)
return 0;
buslatches_setbits(7, BIT(5), 0); // DCLO deasserted
sm_powercycle.state = &sm_powercycle_state_idle;
do_event_initializationsignals() ;
return 1;
static statemachine_state_func sm_powercycle_state_4() {
if (!timeout_reached(TIMEOUT_POWERCYCLE))
return (statemachine_state_func) &sm_powercycle_state_4;
buslatches_setbits(7, BIT(5), 0); // DCLO deasserted
timeout_cleanup(TIMEOUT_POWERCYCLE);
return NULL;
}

View File

@@ -1,46 +1,37 @@
/* pru1_statemachine_powercycle.h: state machine to generate an ACLO/DCLO pseudo power cycle
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_POWERCYCLE_H_
#define _PRU1_STATEMACHINE_POWERCYCLE_H_
#include <stdint.h>
#include "pru1_utils.h" // statemachine_state_func
#define POWERCYCLE_DELAY_MS 100 // wait period in millsecs between ACLO/DCLO transitions
// execution of a state. return : 1, if statemachine stopped
typedef uint8_t (*sm_powercycle_state_func_ptr)(void);
typedef struct {
sm_powercycle_state_func_ptr state; // current state as ptr to "state function"
} statemachine_powercycle_t;
#ifndef _PRU1_STATEMACHINE_POWERCYCLE_C_
extern statemachine_powercycle_t sm_powercycle;
#endif
void sm_powercycle_start();
statemachine_state_func sm_powercycle_start();
#endif

View File

@@ -1,40 +1,42 @@
/* pru1_statemachine_slave.c: state machine for execution of slave DATO* or DATI* cycles
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
Statemachine for execution of slave DATO* or DATI* cycles.
All references "PDP11BUS handbook 1979"
Statemachine for execution of slave DATO* or DATI* cycles.
All references "PDP11BUS handbook 1979"
Slave reponds not to INIT on this level, master must stop bus transactions.
Slave reponds not to INIT on this level, master must stop bus transactions.
- Slave waits for MSYN L->H
- slaves fetches address and control lines
- address is evaluated, ggf mem access
- Slave waits for MSYN L->H
- slaves fetches address and control lines
- address is evaluated, ggf mem access
*/
#define _PRU1_STATEMACHINE_SLAVE_C_
#include <stdlib.h>
#include <stdint.h>
#include "pru1_utils.h"
@@ -46,22 +48,14 @@
#include "pru1_buslatches.h"
#include "pru1_statemachine_slave.h"
statemachine_slave_t sm_slave;
// forwards ;
static uint8_t sm_slave_state_1(void);
static uint8_t sm_slave_state_10(void);
static uint8_t sm_slave_state_20(void);
static uint8_t sm_slave_state_99(void);
// setup with
void sm_slave_start() {
sm_slave.state = &sm_slave_state_1;
// next call to sm_slave.state() starts state machine
}
//statemachine_state_func sm_slave_start(void);
static statemachine_state_func sm_slave_state_10(void);
static statemachine_state_func sm_slave_state_20(void);
static statemachine_state_func sm_slave_state_99(void);
// check for MSYN active
static uint8_t sm_slave_state_1() {
statemachine_state_func sm_slave_start() {
uint8_t latch2val, latch3val, latch4val;
// uint8_t iopage;
uint32_t addr;
@@ -76,10 +70,10 @@ static uint8_t sm_slave_state_1() {
// MSYN active ?
if (!(latch4val & BIT(4)))
return 1; // still idle
return NULL; // still idle
if (latch4val & BIT(5))
// SSYN active: cycle answered by other bus slave
return 1; // still idle
return NULL; // still idle
// checking against SSYN guarantees address if valid if fetched now.
// However, another Bus slave can SSYN immediately
@@ -115,15 +109,13 @@ static uint8_t sm_slave_state_1() {
buslatches_setbyte(5, data & 0xff);
// DATA[8..15] = latch[6]
buslatches_setbyte(6, data >> 8);
//DEBUG_PIN_PULSE ; // trigger scope/LA. auto cleared on next reg_sel
// set SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
sm_slave.state = &sm_slave_state_20;
return (statemachine_state_func) &sm_slave_state_20;
// perhaps PRU2ARM_INTERRUPT now active
} else
// no address match: wait for MSYN to go inactive
sm_slave.state = &sm_slave_state_99;
break;
return (statemachine_state_func) &sm_slave_state_99;
case UNIBUS_CONTROL_DATO:
// fetch data in any case
// DATA[0..7] = latch[5]
@@ -131,17 +123,15 @@ static uint8_t sm_slave_state_1() {
// DATA[8..15] = latch[6]
w |= (uint16_t) buslatches_getbyte(6) << 8;
if (iopageregisters_write_w(addr, w)) {
//DEBUG_PIN_PULSE ; // trigger scope/LA. auto cleared on next reg_sel
// SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
// wait for MSYN to go inactive, then SSYN inactive
sm_slave.state = &sm_slave_state_10;
return (statemachine_state_func) &sm_slave_state_10;
// perhaps PRU2ARM_INTERRUPT now active
} else
// no address match: wait for MSYN to go inactive
sm_slave.state = &sm_slave_state_99;
break;
return (statemachine_state_func) &sm_slave_state_99;
case UNIBUS_CONTROL_DATOB:
// A00 = 1, odd address: get upper byte
// A00 = 0: even address, get lower byte
@@ -157,44 +147,46 @@ static uint8_t sm_slave_state_1() {
// SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
// wait for MSYN to go inactive, then SSYN inactive
sm_slave.state = &sm_slave_state_10;
return (statemachine_state_func) &sm_slave_state_10;
// perhaps PRU2ARM_INTERRUPT now active
} else
// no address match: wait for MSYN to go inactive
sm_slave.state = &sm_slave_state_99;
break;
return (statemachine_state_func) &sm_slave_state_99;
}
return 0; // busy
return NULL; // not reached
}
// End DATO: wait for MSYN to go inactive, then SSYN inactive
// also wait for EVENT ACK
static uint8_t sm_slave_state_10() {
static statemachine_state_func sm_slave_state_10() {
// MSYN = latch[4], bit 4
if (buslatches_getbyte(4) & BIT(4))
return 0; // MSYN still active
if (mailbox.events.eventmask)
return 0; // long SSYN delay until ARM acknowledges all events
return (statemachine_state_func) &sm_slave_state_10; // wait, MSYN still active
if (mailbox.events.event_deviceregister)
// unibusadapter.worker() did not yet run on_after_register_access()
// => wait, long SSYN delay until ARM acknowledges event
return (statemachine_state_func) &sm_slave_state_10;
// if ARM was triggered by event and changed the device state,
// now an Interrupt arbitration may be pending!
// now an Interrupt arbitration may be pending.
// clear SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), 0);
sm_slave.state = &sm_slave_state_1;
return 1; // ready }
return NULL; // ready
}
// End DATI: wait for MSYN to go inactive, then SSYN and DATA inactive
// also wait for EVENT ACK
static uint8_t sm_slave_state_20() {
static statemachine_state_func sm_slave_state_20() {
// MSYN = latch[4], bit 4
if (buslatches_getbyte(4) & BIT(4))
return 0; // MSYN still active
if (mailbox.events.eventmask)
return 0; // long SSYN delay until ARM acknowledges event
return (statemachine_state_func) &sm_slave_state_20; // wait, MSYN still active
if (mailbox.events.event_deviceregister)
// unibusadapter.worker() did not yet run on_after_register_access()
// => wait, long SSYN delay until ARM acknowledges event
return (statemachine_state_func) &sm_slave_state_20;
// if ARM was triggered by event and changed the device state,
// now an Interrupt arbitration may be pending!
// now an Interrupt arbitration may be pending.
// clear first data, then SSYN
// DATA[0..7] = latch[5]
@@ -203,16 +195,14 @@ static uint8_t sm_slave_state_20() {
buslatches_setbyte(6, 0);
// clear SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), 0);
sm_slave.state = &sm_slave_state_1;
return 1; // ready }
return NULL; // ready
}
// end of inactive cycle: wait for MSYN to go inactive
static uint8_t sm_slave_state_99() {
static statemachine_state_func sm_slave_state_99() {
// MSYN = latch[4], bit 4
if (buslatches_getbyte(4) & BIT(4))
return 0; // MSYN still active
return (statemachine_state_func) &sm_slave_state_99; // wait, MSYN still active
sm_slave.state = &sm_slave_state_1;
return 1; // ready }
return NULL; // ready
}

View File

@@ -1,47 +1,35 @@
/* pru1_statemachine_slave.h: state machine for execution of slave DATO* or DATI* cycles
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_SLAVE_H_
#define _PRU1_STATEMACHINE_SLAVE_H_
#include <stdint.h>
#include "pru1_utils.h" // statemachine_state_func
// execution of a state. return : 1, if statemachine stopped
typedef uint8_t (*sm_slave_state_func_ptr)(void);
typedef struct {
sm_slave_state_func_ptr state; // current state as ptr to "state function"
// uint32_t addr; // adress fetched from bus on MSYN
// uint8_t control; // C1,C0 fetched from bus on MSYN
// uint16_t data; // data fetched from bus on MSYN/ to be written to BUS on SSYN
} statemachine_slave_t;
#ifndef _PRU1_STATEMACHINE_SLAVE_C_
extern statemachine_slave_t sm_slave;
#endif
void sm_slave_start();
statemachine_state_func sm_slave_start(void);
#endif

View File

@@ -0,0 +1,111 @@
/* pru1_timeouts.c: timeout conditions
Copyright (c) 2019, 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.
3-jul-2019 JH begin edit
Several timers are needed, but PRU hs only one
global cycle counter CYCLEOUNT. It is 32 bit and runs at 200 MHz,
so rollaround every 21 seconds.
Usage:
- if no timer is running, the first "timeout" reuqest clears CYCLEOUNT
- for each timer, the "timeout" cycle count is set
- tiemr msut be polled for timeout by user.
- a timer is considered "timed-out", if its timeout is 0.
- a global variable regsiteres the active running timeouts.
- a running timeout MUST be canceled, or polled until "timeout_rechaed" !!
The PRU CYCLECOUNT may not be reset if one timeout is active.
So the total run time of all parallel running timeous must not exceed 21 seconds
At least every 21 seconds all timers must be expired.
*/
#include <stdint.h>
#include <string.h>
#include "pru1_utils.h"
#include "pru1_timeouts.h" // own
// count running timers.
static uint8_t timeouts_active = 0;
// cycle end count for each active timeoput.
uint32_t timeout_target_cycles[TIMEOUT_COUNT];
// all functions receive a pointer to one of these array members
#define TIMEOUT_INTERNAL_CYCLES 24
void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles) {
// stop timeout, if already running
if (*target_cycles_var > 0) {
*target_cycles_var = 0;
timeouts_active--; // was inactive
}
if (timeouts_active == 0) {
// first timeout: clear and restart counter
PRU1_CTRL.CTRL_bit.CTR_EN = 0;
PRU1_CTRL.CYCLE = 0;
}
/* 4 cycle used in TIMEOUT_REACHED */
if (delta_cycles < TIMEOUT_INTERNAL_CYCLES)
delta_cycles = 0;
else
delta_cycles -= TIMEOUT_INTERNAL_CYCLES;
*target_cycles_var = PRU1_CTRL.CYCLE + delta_cycles;
PRU1_CTRL.CTRL_bit.CTR_EN = 1;
timeouts_active++; // now one more active
}
// msut be called, if timeout polled anymore for "timeout-reached()
void timeout_cleanup(uint32_t *target_cycles_var) {
if (*target_cycles_var > 0) {
*target_cycles_var = 0;
timeouts_active--; // was inactive
}
}
//
// test a timeout, wehter it reached its arg count nor or earlier
bool timeout_reached(uint32_t *target_cycles_var) {
// fast path: assume timeout_reached() is called
// because timeout is active
if (PRU1_CTRL.CYCLE < *target_cycles_var)
return false;
else if (*target_cycles_var == 0)
return true; // already inactive
else {
// switched from "running" to "timeout reached"
*target_cycles_var = 0;
timeouts_active--;
return true;
}
}
void timeout_init(void) {
timeouts_active = 0;
memset(timeout_target_cycles, 0, sizeof(uint32_t) * TIMEOUT_COUNT);
}

View File

@@ -0,0 +1,50 @@
/* pru1_timeouts.h: timeout conditions
Copyright (c) 2019, 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.
3-jul-2019 JH begin edit
*/
#ifndef _PRU1_TIMEOUTS_H_
#define _PRU1_TIMEOUTS_H_
#include <stdint.h>
#include <stdbool.h>
// predefined timeouts
#define TIMEOUT_COUNT 3
// fixed pointers
#define TIMEOUT_DMA (&timeout_target_cycles[0])
#define TIMEOUT_INIT (&timeout_target_cycles[1])
#define TIMEOUT_POWERCYCLE (&timeout_target_cycles[2])
// cycle end count for each active timeoput.
extern uint32_t timeout_target_cycles[TIMEOUT_COUNT];
// call all functions mit timeout_func(TIMEOUT_*,..)
// This allows the compiler to optimize the timeout_target_cycles[idx] expr
void timeout_init(void);
void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles);
bool timeout_reached(uint32_t *target_cycles_var);
void timeout_cleanup(uint32_t *target_cycles_var);
#endif

View File

@@ -1,38 +1,32 @@
/* pru1_utils.c: misc. utilities
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#define _PRU1_UTILS_C_
#include <stdint.h>
#include "pru1_utils.h"
uint32_t timeout_target ;

View File

@@ -1,32 +1,31 @@
/* pru1_utils.h: misc. utilities
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_UTILS_H_
#define _PRU1_UTILS_H_
#include <stdint.h>
#include <pru_cfg.h>
@@ -36,13 +35,15 @@
volatile register uint32_t __R30;
volatile register uint32_t __R31;
// execution of a state. return : next state; or NULL if statemachine stopped
// return type is void *, but should be statemachine_state_func_ptr recursively
// typedef statemachine_state_func * (*statemachine_state_func)(void);
// Not possible?! So return void * and cast to void *(func(void) on use
typedef void * (*statemachine_state_func)(void);
#define MILLION 1000000L
#define BILLION (1000L * MILLION)
// SSYN timeout after 20 milliseconds
#define UNIBUS_TIMEOUT_PERIOD_US 20
@@ -62,18 +63,18 @@ volatile register uint32_t __R31;
* Translated with "clpru 2.2, -O3"
*
* Test setup:
while (1) {
__R30 |= (1 << 8);
TIMEOUT_SET(NANOSECS(1000)) ; // 1 usec / level
while (!TIMEOUT_REACHED) ;
__R30 &= ~(1 << 8);
TIMEOUT_SET(NANOSECS(1000)) ;
while (!TIMEOUT_REACHED) ;
}
*
* Enhancement: save address of CYLCE in cosntant btabel C28, see
http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-prus-part-1/449
* ALTERNATIVE 1:
while (1) {
__R30 |= (1 << 8);
TIMEOUT_SET(NANOSECS(1000)) ; // 1 usec / level
while (!TIMEOUT_REACHED) ;
__R30 &= ~(1 << 8);
TIMEOUT_SET(NANOSECS(1000)) ;
while (!TIMEOUT_REACHED) ;
}
*
* Enhancement: save address of CYLCE in cosntant btabel C28, see
http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-prus-part-1/449
* ALTERNATIVE 1:
* The Industrial Ethernet module has a general purpose time and compare registers:
* 32 bit Counter is permanetly running cyclic through overlfow
* TIMEOUT_SET: compare0 = counter + delay, clear compare0 flag
@@ -85,35 +86,17 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru
* dann nur compare0 nutzbar
* counter overflow auf 2^32 5ns = 21 sek
* ALTERNATIVE 2:
* PRU0 decrements a mailbox value until it is 0.
* introduces delay in PRU0 outputs!
* ALTERNATIVE 2:
* PRU0 decrements a mailbox value until it is 0.
* introduces delay in PRU0 outputs!
*/
#define TIMEOUT_INTERNAL_CYCLES 24
#define TIMEOUT_SET(cycles) do { \
PRU1_CTRL.CTRL_bit.CTR_EN = 0; \
PRU1_CTRL.CYCLE = 0 ; \
timeout_target = ((cycles) > TIMEOUT_INTERNAL_CYCLES) ? ((cycles) - TIMEOUT_INTERNAL_CYCLES) : 0 ; /* 4 cycle used in TIMEOUT_REACHED */ \
PRU1_CTRL.CTRL_bit.CTR_EN = 1; \
} while(0)
#define TIMEOUT_REACHED \
( (PRU1_CTRL.CYCLE >= timeout_target))
// set PRU1_12 to 0 or 1
#define DEBUG_PIN_SET(val) ( (val) ? (__R30 |= (1 << 12) ) : (__R30 &= ~(1<< 12)) )
#define PRU_DEBUG_PIN(val) ( (val) ? (__R30 |= (1 << 12) ) : (__R30 &= ~(1<< 12)) )
// 20ns pulse an PRU1_12
#define DEBUG_PIN_PULSE do { \
__R30 |= (1 << 12) ; \
__delay_cycles(4-1) ; \
__R30 &= ~(1 << 12) ; \
} while(0)
#define DEBUG_PIN_PULSE_100NS do { \
// 100ns pulse an PRU1_12
#define PRU_DEBUG_PIN_PULSE_100NS do { \
__R30 |= (1 << 12) ; \
__delay_cycles(18) ; \
__R30 &= ~(1 << 12) ; \
@@ -129,8 +112,6 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru
} while(0)
#endif
// To signal the host that we're done, we set bit 5 in our R31
// simultaneously with putting the number of the signal we want
// into R31 bits 0-3. See 5.2.2.2 in AM335x PRU-ICSS Reference Guide.
@@ -142,13 +123,6 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru
#define PRU2ARM_INTERRUPT do { \
/* is Interrupt "level" or "edge"??? */ \
__R31 = PRU2ARM_INTERRUPT_PRU0_R31_VEC_VALID |PRU2ARM_INTERRUPT_SIGNUM ; /* 35 */ \
DEBUG_PIN_PULSE ; \
} while(0)
#ifndef _PRU1_UTILS_C_
extern uint32_t timeout_target ;
#endif
#endif

View File

@@ -38,26 +38,25 @@ public:
/* these values are generated by prussdrv functions */
// base address of shared DDR memory, in ARM Linux memory space
volatile ddrmem_t *base_virtual;
uint32_t len;// size of allocated range as given by UIO driver
uint32_t len; // size of allocated range as given by UIO driver
// physical ddrmem_base address, for access by PRU
uint32_t base_physical;
// emulated address range
bool enabled = false ; // true if startaddr <= endaddr
bool enabled = false; // true if startaddr <= endaddr
uint32_t unibus_startaddr;
uint32_t unibus_endaddr;
ddrmem_c() ;
ddrmem_c();
void info(void);
void save(char *fname);
void load(char *fname);
void clear(void);
void fill_pattern(void);
void fill_pattern_pru(void);
void unibus_slave(uint32_t startaddr, uint32_t endaddr);
bool set_range(uint32_t startaddr, uint32_t endaddr);
bool deposit(uint32_t addr, uint16_t w);
bool exam(uint32_t addr, uint16_t *w);
void unibus_slave(uint32_t startaddr, uint32_t endaddr);bool set_range(uint32_t startaddr,
uint32_t endaddr);bool deposit(uint32_t addr, uint16_t w);bool exam(uint32_t addr,
uint16_t *w);
};
#ifndef _DDRMEM_C_
@@ -78,7 +77,6 @@ extern ddrmem_c *ddrmem;
#define DDRMEM_MEMGET_W(addr) \
( mailbox.ddrmem_base_physical->memory.words[(addr)/2] )
void ddrmem_fill_pattern(void);
#endif

View File

@@ -1,28 +1,28 @@
/* iopageregister.h: Tables of implemented devices and their registers.
Datastructures common to ARM and PRU.
/* iopageregister.h: Tables of implemented devices and their registers.
Datastructures common to ARM and PRU.
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered Beta phase
Implementation of UNIBUS devices:
@@ -116,7 +116,6 @@
// #define MAX_REGISTERS_PER_DEVICE 32 // RK611 has the most?
#define MAX_DEVICE_HANDLE 255 // 0 not used, must fit in 8bits
// Bitmask: Create event fpr iopagergister DATI/DATO access ?
#define IOPAGEREGISTER_EVENT_FLAG_DATI 0x01
#define IOPAGEREGISTER_EVENT_FLAG_DATO 0x02

View File

@@ -1,29 +1,28 @@
/* mailbox.h: Command and status data structures common to ARM and PRU
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _MAILBOX_H_
#define _MAILBOX_H_
@@ -32,7 +31,7 @@
#include "unibus.h"
// arm to pru
#define ARM2PRU_NONE 0 // Operation complete: don't change
#define ARM2PRU_NONE 0 // Operation complete: must be 0!
#define ARM2PRU_NOP 1 // to check wether PRU is running
#define ARM2PRU_HALT 2 // run PRU1 into halt
#define ARM2PRU_MAILBOXTEST1 3
@@ -43,12 +42,14 @@
#define ARM2PRU_BUSLATCH_TEST 8 // read a mux register
#define ARM2PRU_INITPULSE 9 // pulse UNIBUS INIT
#define ARM2PRU_POWERCYCLE 10 // ACLO/DCLO power cycle simulation
#define ARM2PRU_DMA_ARB_NONE 11 // DMA without NPR/NPG/SACK arbitration
#define ARM2PRU_DMA_ARB_CLIENT 12 // DMA with arbitration by external Arbitrator
#define ARM2PRU_DMA_ARB_MASTER 13 // DMA as Arbitrator
#define ARM2PRU_DDR_FILL_PATTERN 14 // fill DDR with test pattern
#define ARM2PRU_DDR_SLAVE_MEMORY 15 // use DDR as UNIBUS slave memory
#define ARM2PRU_INTR 16 // INTR, only with arbitration
#define ARM2PRU_ARB_MODE_NONE 11 // DMA without NPR/NPG/SACK arbitration
#define ARM2PRU_ARB_MODE_CLIENT 12 // DMA with arbitration by external Arbitrator
#define ARM2PRU_ARB_MODE_MASTER 13 // DMA as Arbitrator
#define ARM2PRU_DMA 14 // DMA with selcted arbitration
#define ARM2PRU_INTR 15 // INTR with arbitration by external Arbitrator
#define ARM2PRU_INTR_CANCEL 16 // clear INTR which has been requested
#define ARM2PRU_DDR_FILL_PATTERN 17 // fill DDR with test pattern
#define ARM2PRU_DDR_SLAVE_MEMORY 18 // use DDR as UNIBUS slave memory
// possible states of DMA machine
#define DMA_STATE_READY 0 // idle
@@ -58,12 +59,17 @@
#define DMA_STATE_INITSTOP 4 // stop because INIT signal sensed
// bits BR*/NPR interrupts in buslatch 0 and 1
#define ARBITRATION_PRIORITY_BIT_B4 0x01
#define ARBITRATION_PRIORITY_BIT_B5 0x02
#define ARBITRATION_PRIORITY_BIT_B6 0x04
#define ARBITRATION_PRIORITY_BIT_B7 0x08
#define ARBITRATION_PRIORITY_BIT_NP 0x10
#define ARBITRATION_PRIORITY_MASK 0x1f
// bit # is index into arbitration_request[] array.
#define PRIORITY_ARBITRATION_BIT_B4 0x01
#define PRIORITY_ARBITRATION_BIT_B5 0x02
#define PRIORITY_ARBITRATION_BIT_B6 0x04
#define PRIORITY_ARBITRATION_BIT_B7 0x08
#define PRIORITY_ARBITRATION_BIT_NP 0x10
#define PRIORITY_ARBITRATION_INTR_MASK 0x0f // BR4|BR5|BR6|BR7
#define PRIORITY_ARBITRATION_BIT_MASK 0x1f
// data for a requested DMA operation
#define PRU_MAX_DMA_WORDCOUNT 512
#include "ddrmem.h"
@@ -87,13 +93,12 @@ typedef struct {
#define MAILBOX_BUSLATCH_EXERCISER_PATTERN_COUNT 4
typedef struct {
uint8_t pattern ; // input: which access pattern?
uint8_t addr[8] ; // access sequence of register addresses
uint8_t writeval[8] ; // data value for each
uint8_t readval[8] ; // read back results
uint8_t pattern; // input: which access pattern?
uint8_t addr[8]; // access sequence of register addresses
uint8_t writeval[8]; // data value for each
uint8_t readval[8]; // read back results
} mailbox_buslatch_exerciser_t;
typedef struct {
uint8_t addr_0_7; // start values for test sequence
uint8_t addr_8_15;
@@ -102,27 +107,35 @@ typedef struct {
} mailbox_buslatch_test_t;
// data for a requested DMA operation
#define MAX_DMA_WORDCOUNT 512
typedef struct {
// take care of 32 bit word borders for struct members
uint8_t cur_status; // 0 = idle, 1 = running, 2 = timeout error
uint8_t control; // cycle to perform: only DATO, DATI allowed
uint16_t wordcount; // # of remaining words transmit/receive, static
uint32_t cur_addr; // current address in tranwfer, if timeout: offending address.
// ---dword---
uint32_t cur_addr; // current address in transfer, if timeout: offending address.
// if complete: last address accessed.
uint32_t startaddr; // address of 1st word to transfer
uint16_t words[MAX_DMA_WORDCOUNT]; // buffer for rcv/xmt data
uint16_t words[PRU_MAX_DMA_WORDCOUNT]; // buffer for rcv/xmt data
} mailbox_dma_t;
// data for all 4 pending INTR requests
// vector for an INTR transaction
typedef struct {
uint16_t vector; // interrupt vector to be transferred
uint8_t priority_bit; // one of ARBITRATION_PRIORITY_B[4-7]
} mailbox_intr_t;
/* all requested INTRs */
uint16_t vector[4]; // interrupt vectors for BR4..7 to be transferred
// ---dword---
// event triggered on UNIBUS access to "active" device registers
// set by PRU, read by ARM on event. Bitmask.
#define EVENT_DEVICEREGISTER 0x01
#define EVENT_INITIALIZATIONSIGNALS 0x02
/* data for currently requested with ARM2PRU_INTR */
uint8_t priority_arbitration_bit; // PRIORITY_ARBITRATION_BIT_*
uint8_t level_index; // newly requested BR*. 0 = BR4, ... 3 = BR7
// interrupt register state to be set atomically with BR line
uint16_t iopage_register_value;
// ---dword---
uint8_t iopage_register_handle;
uint8_t _dummy1, _dummy2, _dummy3;
// multiple of 32 bit now
} mailbox_intr_t;
// states of initialization section lines. Bitmask = latch[7]
#define INITIALIZATIONSIGNAL_INIT (1 << 3)
@@ -130,20 +143,47 @@ typedef struct {
#define INITIALIZATIONSIGNAL_DCLO (1 << 5)
typedef struct {
uint8_t eventmask; // bitwise. triggered, 0 = invalid/ACKEed by ARM
/*** EVENT_DEVICEREGISTER ***/
// trigger flags raised by PRU, reset by ARM
// differemt events can be raised asynchronically and concurrent,
// but a single event type is sequentially raised by PRU and cleared by ARM.
/* Access to device register ***/
uint8_t event_deviceregister; // trigger flag
// info about register access
uint8_t unibus_control; // DATI,DATO,DATOB
// handle of controller
uint8_t device_handle;
// # of register in device space
uint8_t device_register_idx;
// ---dword---
// UNIBUS address accessed
uint32_t addr; // accessed address: odd/even important for DATOB
uint16_t data ; // data vale for DATO event
// ---dword---
uint16_t data; // data value for DATO event
/*** EVENT_INITIALIZATIONSIGNALS ***/
/*** DMA transfer complete
After ARM2PRU_DMA_*, NPR/NPG/SACK protocll was executed and
Data trasnfered accoring to mailbox_dma_t.
After that, mailbox_dma_t is updated and signal raised.
*/
uint8_t event_dma; // trigger flag
/*** Event priority arbitration data transfer complete
After ARM2PRU_INTR, one of BR4/5/6/7 NP was requested,
granted, and the data transfer was handled as bus master.
*/
uint8_t _dummy1;
// ---dword---
uint8_t event_intr; // trigger flag: 1 = one of BR4,5,6,7 vector on UNIBUS
uint8_t event_intr_level_index; // 0..3 -> BR4..BR7
uint8_t _dummy2, _dummy3;
// ---dword---
/*** INIT or Power cycle seen on UNIBUS ***/
uint8_t event_init; // trigger flag
uint8_t event_power; // trigger flag
uint8_t initialization_signals_prev; // on event: a signal changed from this ...
// ---dword---
uint8_t initialization_signals_cur; // ... to this
// uint8_t dummy[2]; // make record multiple of dword !!!
@@ -161,14 +201,16 @@ typedef struct {
// set by PRU, read by ARM on event
mailbox_events_t events;
mailbox_intr_t intr;
mailbox_dma_t dma;
// data structs for misc. opcodes
union {
mailbox_test_t mailbox_test;
mailbox_buslatch_t buslatch;
mailbox_buslatch_test_t buslatch_test;
mailbox_buslatch_exerciser_t buslatch_exerciser;
mailbox_dma_t dma;
mailbox_intr_t intr;
mailbox_buslatch_exerciser_t buslatch_exerciser;
};
} mailbox_t;
@@ -188,7 +230,7 @@ extern volatile mailbox_t *mailbox;
void mailbox_print(void);
int mailbox_connect(void);
void mailbox_test1(void);
void mailbox_execute(uint8_t request, uint8_t stopcode);
void mailbox_execute(uint8_t request);
#else
// included by PRU code
@@ -204,8 +246,9 @@ extern volatile far mailbox_t mailbox;
mailbox.events.device_handle = _reg->event_device_handle ;\
mailbox.events.device_register_idx = _reg->event_device_register_idx ; \
mailbox.events.addr = _addr ; \
mailbox.events.data = _data ; \
mailbox.events.eventmask |= EVENT_DEVICEREGISTER ; /* data for ARM valid now*/ \
mailbox.events.data = _data ; \
mailbox.events.event_deviceregister = 1 ; \
/* data for ARM valid now*/ \
PRU2ARM_INTERRUPT ; \
/* leave SSYN asserted until mailbox.event.signal ACKEd to 0 */ \
} while(0)

View File

@@ -1,28 +1,28 @@
/* pru_pru_mailbox.h: structure for data exchange between PRU0 and PRU1
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU_PRU_MAILBOX_H_
#define _PRU_PRU_MAILBOX_H_
@@ -30,16 +30,15 @@
#include <stdint.h>
typedef struct {
// just the state of PRU0 R30 output.
// just the state of PRU0 R30 output.
// PRU1 writes the value
// PRU0 monitors and copies to its local R30 = REG_DATA_OUT pins
uint32_t xxx_pru0_r30 ;
} pru_pru_mailbox_t ;
uint32_t xxx_pru0_r30;
} pru_pru_mailbox_t;
#ifndef _PRU_PRU_MAILBOX_C_
extern volatile pru_pru_mailbox_t pru_pru_mailbox ;
extern volatile pru_pru_mailbox_t pru_pru_mailbox;
#endif
#endif

View File

@@ -1,30 +1,28 @@
/* tuning.h: Constants to adapt UNIBUS functions
Copyright (c) 2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2019, 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:
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 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.
7-jun-2019 JH entered beta phase
*/
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.
7-jun-2019 JH entered beta phase
*/
#define TUNING_PCB_LEGACY_SECURE
//#define TUNING_PCB_2018_12_OPTIMIZED
@@ -34,50 +32,47 @@
// A BBB with optimized terminators can reach 8
// BBG can reach *ALMOST* 9
// #define BUSLATCHES_GETBYTE_DELAY 10 // Standard
#if defined(TUNING_PCB_TEST)
// experimental to test error rates
#define BUSLATCHES_GETBYTE_DELAY 10
#define BUSLATCHES_SETBITS_DELAY 4
#define BUSLATCHES_SETBYTE_DELAY 6
// experimental to test error rates
#define BUSLATCHES_GETBYTE_DELAY 10
#define BUSLATCHES_SETBITS_DELAY 4
#define BUSLATCHES_SETBYTE_DELAY 6
#elif defined(TUNING_PCB_LEGACY_SECURE)
/* Secure setting for PCBs <= 2018-12, delivered before June 2019.
Necessary for longtime ZKMA on critical PCBs.
BeagleBone: BBB (no BBG)
U2 (REGSEL): 74AC138
RN8,9 (DATIN) : 47
RN10 <1:6>(REGADR): 33
RN10 <7:8>(REGWRITE): 33
R6,R7 (REGWRITE TERM): none
RN6,RN7 (DATOUT inline): 22
RN4,RN5 [[/DATOUT]] end) -> 1K/-
*/
#define BUSLATCHES_GETBYTE_DELAY 11
#define BUSLATCHES_SETBITS_DELAY 5
#define BUSLATCHES_SETBYTE_DELAY 7
/* Secure setting for PCBs <= 2018-12, delivered before June 2019.
Necessary for longtime ZKMA on critical PCBs.
BeagleBone: BBB (no BBG)
U2 (REGSEL): 74AC138
RN8,9 (DATIN) : 47
RN10 <1:6>(REGADR): 33
RN10 <7:8>(REGWRITE): 33
R6,R7 (REGWRITE TERM): none
RN6,RN7 (DATOUT inline): 22
RN4,RN5 [[/DATOUT]] end) -> 1K/-
*/
#define BUSLATCHES_GETBYTE_DELAY 11
#define BUSLATCHES_SETBITS_DELAY 5
#define BUSLATCHES_SETBYTE_DELAY 7
#elif defined(TUNING_PCB_2018_12_OPTIMIZED)
/* Setting for PCB v2018_12 with optimized timing (ticket 21, June 2019)
BeagleBone: BBB (no BBG)
U2 (REGSEL): 74AC138 -> 74AHC138
RN8,9 (DATIN) : 47 -> 68 Ohm
RN10 <1:6>(REGADR): 33->0 Ohm
RN10 <7:8>(REGWRITE): 33->0 Ohm
R6,R7 (REGWRITE TERM): none
RN6,RN7 (DATOUT inline): 22 -> 27
RN4,RN5 [[/DATOUT]] end) -> 180/-
*/
#define BUSLATCHES_GETBYTE_DELAY 9
#define BUSLATCHES_SETBITS_DELAY 4
#define BUSLATCHES_SETBYTE_DELAY 6
//#define BUSLATCHES_GETBYTE_DELAY 8
//#define BUSLATCHES_SETBITS_DELAY 3
//#define BUSLATCHES_SETBYTE_DELAY 5
/* Setting for PCB v2018_12 with optimized timing (ticket 21, June 2019)
BeagleBone: BBB (no BBG)
U2 (REGSEL): 74AC138 -> 74AHC138
RN8,9 (DATIN) : 47 -> 68 Ohm
RN10 <1:6>(REGADR): 33->0 Ohm
RN10 <7:8>(REGWRITE): 33->0 Ohm
R6,R7 (REGWRITE TERM): none
RN6,RN7 (DATOUT inline): 22 -> 27
RN4,RN5 [[/DATOUT]] end) -> 180/-
*/
#define BUSLATCHES_GETBYTE_DELAY 9
#define BUSLATCHES_SETBITS_DELAY 4
#define BUSLATCHES_SETBYTE_DELAY 6
//#define BUSLATCHES_GETBYTE_DELAY 8
//#define BUSLATCHES_SETBITS_DELAY 3
//#define BUSLATCHES_SETBYTE_DELAY 5
#endif
// UNIBUS timing: Wait to stabilize DATA before MSYN asserted
// per DEC spec
// #define UNIBUS_DMA_MASTER_PRE_MSYN_NS 150

View File

@@ -1,31 +1,30 @@
/* unibus.h: shared structs used in PRU and ARM
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
19-may-2018 JH created
*/
12-nov-2018 JH entered beta phase
19-may-2018 JH created
*/
#ifndef _UNIBUS_H_
#define _UNIBUS_H_
@@ -34,13 +33,12 @@
#define UNIBUS_WORDCOUNT 0x20000 // 128KiW = 256 KiB
// bus transaction. can be directly assigned to lines C1,C0
#define UNIBUS_CONTROL_DATI 0x00 // 16 bit word from slave to master
#define UNIBUS_CONTROL_DATIP 0x01 // DATI, inhibts core restore. DATO must follow.
#define UNIBUS_CONTROL_DATO 0x02 // 16 bit word from master to slave
#define UNIBUS_CONTROL_DATOB 0x03 // 8 bit byte from master to slave
// data<15:8> for a00 = 1, data<7:0> for a00 = 0
// data<15:8> for a00 = 1, data<7:0> for a00 = 0
#define UNIBUS_CONTROL_ISDATO(c) (!!((c) & 0x02)) // check for DATO/B
#define UNIBUS_TIMEOUTVAL 0xffffffff // EXAM result for bus timeout
@@ -51,27 +49,29 @@
// pru c-compile is NOT called with --endian=big
typedef struct {
union {
uint16_t words[UNIBUS_WORDCOUNT] ;
uint8_t bytes[2*UNIBUS_WORDCOUNT] ;
} ;
uint16_t words[UNIBUS_WORDCOUNT];
uint8_t bytes[2 * UNIBUS_WORDCOUNT];
};
} unibus_memory_t;
#ifdef ARM
// included only by ARM code
#include "logsource.hpp"
#include "utils.hpp"
// parameter and functions for low level UNIBUS control
class dma_request_c;
class intr_request_c;
class unibus_c: public logsource_c {
public:
enum arbitration_mode_enum {
ARBITRATION_MODE_NONE = 0, // no BR*/BG*, NR/NPG SACK protocoll
ARBITRATION_MODE_CLIENT = 1, // external Arbitrator (running PDP-11 CPU) required
ARBITRATION_MODE_MASTER = 2 // implmenet Arbitrator
// with or without physical CPU for arbitration
} ;
enum arbitration_mode_enum {
ARBITRATION_MODE_NONE = 0, // no BR*/BG*, NR/NPG SACK protocoll
ARBITRATION_MODE_CLIENT = 1, // external Arbitrator (running PDP-11 CPU) required
ARBITRATION_MODE_MASTER = 2 // implmenet Arbitrator
// with or without physical CPU for arbitration
};
private:
@@ -79,40 +79,43 @@ private:
public:
// percent of time to be used for DMA master cycles
unsigned dma_bandwidth_percent ;
// default size of dma block transfers
unsigned dma_wordcount ;
unibus_c();
~unibus_c();
unibus_c() ;
static char *control2text(uint8_t control) ;
static char *control2text(uint8_t control);
static char *data2text(unsigned val);
void init(void);
void powercycle(void) ;
static void set_arbitration_mode(enum arbitration_mode_enum arbitration_mode);
void interrupt(uint8_t priority, uint16_t vector) ;
bool dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_t control, uint32_t startaddr,
unsigned blocksize);
void powercycle(void);
void mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode,
uint16_t *words, uint32_t start_addr,
uint32_t end_addr, unsigned blocksize, bool *timeout) ;
void mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode,
uint16_t *words, unsigned start_addr,
unsigned end_addr, unsigned blocksize, bool *timeout) ;
// functions of unibusadapter to do simple DMA
dma_request_c *dma_request;
//intr_request_c *intr_request;
uint32_t test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode) ;
bool dma(enum unibus_c::arbitration_mode_enum arbitration_mode, bool blocking, uint8_t control,
uint32_t startaddr, uint16_t *buffer, unsigned wordcount);
void mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words,
uint32_t unibus_start_addr, uint32_t unibus_end_addr, bool *timeout);
void mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words,
unsigned unibus_start_addr, unsigned unibus_end_addr, bool *timeout);
void mem_access_random(enum unibus_c::arbitration_mode_enum arbitration_mode,
uint8_t unibus_control, uint16_t *words, uint32_t unibus_start_addr,
uint32_t unibus_end_addr, bool *timeout, uint32_t *block_counter);
uint32_t test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode);
uint16_t testwords[UNIBUS_WORDCOUNT];
void test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, uint32_t start_addr, uint32_t end_addr, unsigned mode) ;
void test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, uint32_t start_addr,
uint32_t end_addr, unsigned mode);
};
extern unibus_c *unibus ;
extern unibus_c *unibus;
#endif /* ARM */

View File

@@ -1,96 +0,0 @@
Unibone testplan
Platform: PDP11/34.
Plug UniBone in Slot with NPR/NPG unconnected (CA1 =!= CA2)
1. Slave memory
UniBone implements a memory board.
a) Slave cycles
implement 256Kb 000000-777776 memory.
Boot XXDP2.5, run ZKMA?? with SW= 010000
-> 256KB tested
b) memory layout
Assert memors layout udner ARM = memory layout for PDP-11
load "Papertape Basic" image into DDR RAM
start at 16104 -> BASCI prompt?
c) Address logic
Add 32KB MS11-J board M7847 (00000-077776)
UniBone implements 100000-777776 memory.
Boot XXDP2.5, run ZKMA?? with SW= 010000
-> 256KB tested
2. Bus Master only without arbitration
UniBone performs bus master DMA cycles.
No slave memory emulation, no NPR/G/SACK arbitration.
- add PDP-11 memory board
- Halt CPU (will crash if Unibone generates Bus cycles)
a) single cycle: Set Switchreg to 123456
Unibone: EXAM 777570 ->12345
EXAM next -> Bustimeout
b) perform memory test on PDP-11 memory
3. Bus Master with feed back to internal salve, without arbitration
If master cycles hit addresses for emualted memory, slave logic anwsers master cycles,
This could be an internal operation, but UniBus signals MSYN/SSYN/Control/DATA/ADDtr are generated
- add 32KB board. Emulates slave memory 100000-777776.
a) UniBone runs memory test
Watch UNIBUS signals: access to real mmeoryy at 00000-077776 should look like itnernal
access to 100000-777776.
4.) Bus master DMA with arbitration
Like 3., but NPR/NPG/SACK arbitration is performed
- plug UniBone in 11/34 between KX11 console and RL11 controller.
Card order: CPU - KY11 - UniBone - RL11
- Confirm that GRANT Chain (CA1/CB1) is open in UniBone backplane slot
- remove jumper between NPG_IN-NPG_OUT
- start Unibone emulation,
a) Boot from RL11. RL11 performs DMA, this works only if UniBone
"connects" NPG_IN to NPG_OUT actively
b) PDP11GUI memory test on lower 56kb 000000-157776
Perform Busmaster memory test per DMA parallel in upper memory- 200000..757776
"tr 200000 777777"
-> lots of NPG transaction in parallel with regualr operation
c) Same as b), but memory test with XXDP ZKMA on lower 56kb 000000-177776 (SR=000000)
- load zkmaf0_200 into memory
- on M9312 console:
@L 200
@G
d) NPR priority: 11/34 KY Programmer console:
HALT -> KY11 is busmaster and holds SACK
UniBone Exam -> seems to hang, as KY11 holds SACK
KY11: CONT -> start CPU, release SACK -> UniBone EXAM gets completed
5) Interrupts
Use "ti" test option and "intrtst" PDP-11 Programm.
Issue interrupts at differnt levels,
combine with different processor pririoty levels.

View File

@@ -0,0 +1,185 @@
# "demo" test sequences for basic interrupt test
#
# Testprogram "intrtst" evaluates chars from DL11,
# but input can be given by deposit char to 7776.
ti # enter menu
pwr # restart PDP-11
.wait 1000
m # emulate missing memory
ll intrtst.lst # load PDP-11 test program
.print Now start Program on PDP-11.
.print On M9312: "L <startaddr>" , "S"
.input
.print First all tests with looong blocking ISRs in "Slow" mode
d 7776 'S
.print *************************************************************************
.print *** Test 1: single INTR
.print 4 Interrupts raised in order
.print Set CPU level to 0 (all levels BR4..BR7 enabled)
d 7776 '0
.wait 1000
i 4 40
.wait 250
i 5 50
.wait 250
i 6 60
.wait 250
i 7 70
.wait 250
.print PDP-11 should respond "<ISR 40>" "<ISR 50>" "<ISR 60>" "<ISR 70>"
.input
.print *************************************************************************
.print *** Test 2: INTR Priority
.print CPU INTR disabled, 4 Interrupts raised and not processed
.print
.print Set CPU level to 7 (all levels BR4..BR7 disabled)
d 7776 '7
.wait 1000
i 4 40
i 5 50
i 6 60
i 7 70
.print PDP-11 should raise no ISRs.
.print Set CPU level to 6, PDP-11 should respond "ISR 70"
d 7776 '6
.wait 1000
.print Set CPU level to 0
d 7776 '0
.print PDP-11 should respond "ISR 60" "ISR 50" "ISR 40"
.input
.print *************************************************************************
.print *** Test 3: raise INTR while others are processed
.print The printout "ISR <vector>" needs some time and is done on CPU priority level 7
.print (with INTR disabled). In that time other ISR are requested
.print Set CPU level to 0 (all levels BR4..BR7 enabled)
d 7776 '0
.wait 1000
i 4 40
i 6 60
i 5 50
i 7 70
.print PDP-11 should respond "ISR 40" "ISR 70" "ISR 60" "ISR 50"
.input
.print *************************************************************************
.print *** Test 4: raise INTR multiple
.print The printout "ISR <vector>" needs some time and is done on CPU priority level 7
.print (with INTR disabled). In that time INTR are re-raised again.
.print This is a "CPU interrupt overload" scenario, ISRs are not called as often
.print as required.
.print Set CPU level to 0 (all levels BR4..BR7 enabled)
d 7776 '0
.wait 1000
i 4 40 # accepted, ISR called blocking
i 7 70 # ISR 70 scheduled
i 7 70 # re-raise ignored
i 7 70 # re-raise ignored
i 4 40 # ISR 40 scheduled
i 4 40 # re-raise ignored
i 7 70 # re-raise ignored
i 4 40 # re-raise ignored
i 4 40 # re-raise ignored
i 4 40 # re-raise ignored
# end ISR 40: call ISR 70 (highest prio), then ISR 40-
.print PDP-11 should call only two ISRs:"ISR 40", "ISR 70" "ISR 40"
.input
.print *************************************************************************
.print *** Test 5: INTR Slot priority
.print Test: raise INTR while others are processed
.print The printout "ISR <vector>" needs some time and is done on CPU priority level 7
.print (with INTR) disabled. In that time other ISR are requested and waiting.
.print Set CPU level to 7 (all levels BR4..BR7 disabled)
d 7776 '7
.wait 1000
# i <level> <slot> <vector>
i 4 1 110
i 4 2 120
i 5 10 70
i 5 11 100
i 6 20 50
i 6 21 60
i 7 14 40 # is first (waiting on PRU, highest prio), not sorted as not blocked
i 7 12 20
i 7 11 10
i 7 13 30
.print Set CPU level to 0
d 7776 '0
.print PDP-11 should process ISRs sorted first by level, then by slot:"ISR 40, 10, 20, ... 120"
.input
.print *** Test 6: repeat INTR Slot priority, fast ISR
d 7776 'F
d 7776 '7
.wait 1000
# i <level> <slot> <vector>
i 4 1 110 # is #4
i 4 2 120
i 5 10 70 # is #3
i 5 11 100
i 6 20 50 # is #2 (waiting on PRU, prio after 7)
i 6 21 60
i 7 14 40 # is #1 (waiting on PRU, highest prio), not sorted as not blocked
i 7 12 20
i 7 11 10
i 7 13 30
.print Set CPU level to 0
d 7776 '0
.print PDP-11 should process ISRs sorted first by level, then by slot:"ISR 40, 10, 20, ... 120"
d 7776 'F # display ISR log
.input
.print *************************************************************************
.print *** Test 7: DMA priority
.print Start multi-chunk DMA "1" on less priorized slot,
.print then one "2" in priorized slot into same memory.
.print DMA "2" is interrupting DMA "1" on firstchunk of "1"
# dma is non blocking and DEPOSITing
# dma <channel> <from> <to> <fill>
# channel #0 has higher slot prority than #1
dma 1 100000 757776 1
dma 0 100000 757776 2
.wait 1000 # wait for DMAs to complete
.print Check: So first data words are "2" (DMA channel 1 runs 2nd).
e 100000
.print Last data words are "1" (DMA channel 1 finished later and overwrote channel 0's data)
e 757776
.input
d 7776 '0 # all INTR enabled
.wait 1000
d 7776 'S # slow ISR is printing text
.wait 1000
.print *************************************************************************
.print Test 8: DMA and INTR mixed
.print Set CPU level to 0 (all levels BR4..BR7 enabled)
.print Verify in logic analyzer, that DMA interupts
.print while ISR 70 is printing its text.
.print Trigger LA on 2nd SACK H->L
.input
i 7 70
dma 0 100000 757776 123 # trigger on 2nd SACK
i 4 40
.print Is Interrupt BR4 40 services after first DMA chunk or at the end (both OK!)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,11 +7,18 @@
; 1.1. print a start message,
; 1.2. echoes chars typed to the output until ^C is hit
; Chars 0..7 set the new processor priority level.
; 1.3. prints an end message and HALTs.
; 1.3 prints an end message and HALTs.
; 1.4. on CONT it repeats.
;
; 2. For INTR test, the 256 vectors 0,4,10,14,..374 each print
; a string on interrupt.
; In "Slow" mode, the ISR prints the vector directly
; (ISR rountine > 100ms)
; In "Fast" mode, the ISR logs the vector in a list
; (It can be printed later with "F"
;
; As alternative to input over DL11,
; "keyboard input chars" can be deposited into 7776
;
; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com
@@ -63,6 +70,12 @@ isr'vecidx:
; ---- foreground thread ---------
.=0
jmp @#start ; easier manual start from 0
.=7776
inchr: .word ; input alternative to DL11
.=10000
stack = . - 2 ; stack growns down from start
@@ -71,6 +84,10 @@ start:
mov #stack,sp ; init stack
clr @#psw ; clear priority, allow all interupts
reset ; disable INTR on all devices
clr @#logptr ; default: slow mode
; 1. print "Hello" msg
mov #shello,r1
call @#puts
@@ -108,9 +125,24 @@ start:
asl r0
mov r0,@#psw
br 1$
br 1$ ; OK, next char
2$:
2$: ; -- eval "S", F"
cmpb r0,#'S
bne 3$
mov #sslwmd,r1 ; "slow mode"
call @#puts
clr @#logptr
br 1$ ; OK, next char
3$: cmpb r0,#'F
bne 8$ ;
mov #sfstmd,r1 ; "fast mode"
call @#puts
call @#prtlog ; print logged vectors, if any
mov #logbuf,@#logptr; pointer to biuffer start
br 1$ ; OK, next char
8$:
call @#putc ; no: echo char in r0 and loop
br 1$
@@ -137,14 +169,25 @@ doisr:
mov r1,-(sp)
mov r0,-(sp)
; print msg
mov @#logptr,r1
beq 1$
; Fast mode: log vector
mov r0,(r1)+ ; store vector in array
mov r1,@#logptr ; save updated list pointer
br 9$
1$: ; "Slow mode: print msg
mov #sisr1,r1 ; "ISR "
call @#puts
mov (sp),r0 ; restore vecnum
call @#putnum
mov #sisr2,r1 ; "cr lf
call @#puts
; make ISR 100ms
mov #144,r0 ; 100
call @#waitms
9$:
mov (sp)+,r0
mov (sp)+,r1
mov (sp)+,r2
@@ -153,6 +196,28 @@ doisr:
return
; ----------------------
; prtlog - print log of isr vectors
; word list from logbuf to logptr
prtlog:
tst @#logptr
beq 9$ ; ptr 0, slow mode, nothing logged
mov #logbuf,r2
1$:
cmp r2,@#logptr
bhis 8$ ; end of list reached
mov (r2)+,r0 ; print vector from list
call @#putnum
mov #40,r0 ; print space separator
call @#putc
br 1$
8$:
mov #scrlf,r1 ; CR/LF
call @#puts
9$:
return
; ----------------------
; puts - print a string
; r1 = pointer, r0,r1 changed
@@ -169,6 +234,10 @@ puts:
numbf0: .blkw 10 ; space to mount number string
numbf1 =.
putnum:
mov r0,-(sp)
mov r1,-(sp)
mov r2,-(sp)
mov r3,-(sp)
mov r0,r2 ; r2 = shifter
mov #numbf1,r1 ; r1 = buffer pointer, backwards
movb #0,-(r1) ; set terminating 0
@@ -181,12 +250,16 @@ putnum:
add #60,r0 ; r0 += '0'
movb r0,-(r1) ; write in buffer
clc
ror r2 ; shift to next digit
ror r2
ror r2
asr r2 ; shift to next digit
asr r2
asr r2
sob r3,1$ ; loop for all 6 digits
call @#puts
mov (sp)+,r3
mov (sp)+,r2
mov (sp)+,r1
mov (sp)+,r0
return
@@ -206,11 +279,28 @@ putc:
; result in r0, r4 changed
getc:
mov #dladr,r4 ; set base addr
1$: tstb (r4) ; RCVR DONE?
1$: mov @#inchr,r0 ; external DEPOSIT into inchr?
bne 9$ ; yes: process as input
tstb (r4) ; else: RCVR DONE?
bpl 1$ ; no, loop
mov 2(r4),r0 ; return data
9$:
clr @#inchr
return
; ----------------------
; waitms - wait r0 milli seconds
waitms:
mov r1,-(sp)
1$: ; -- outer loop
mov #764,r1 ; 500
2$: ; -- inner loop: 1ms @ 1MHz
sob r1,2$ ; 1 cycle,2 us per loop (11/34)
sob r0,1$
mov (sp)+,r1
return
; ---- Test strings, each 256 chars max ---------
@@ -233,11 +323,25 @@ shello:
.byte 15,12 ; CR, LF,
.ascii /Chars 0..7 set the new processor priority level./
.byte 15,12 ; CR, LF,
.ascii /S = slow mode: ISR prints message (default)/
.byte 15,12 ; CR, LF,
.ascii /F = fast mode: ISR logs vector, print and clr current log/
.byte 15,12 ; CR, LF,
.byte 0 ; NUL=end marker
sslwmd:
.ascii /Slow mode: called ISR prints vector directly/
.byte 15,12 ; CR, LF,
.byte 0 ; NUL=end marker
sfstmd:
.ascii /Fast mode: called ISR vectors are logged:/
.byte 15,12 ; CR, LF,
.byte 0 ; NUL=end marker
sprio0:
.byte 15,12 ; CR, LF,
.ascii /CPU priority now /
.ascii /CPU priority level now /
.byte 0
sprio1:
@@ -247,7 +351,16 @@ sprio1:
sbye:
.byte 15,12
.ascii /Good Bye!/
scrlf:
.byte 15,12,0 ; CR, LF, NUL=end marker
; .byte 0 ; make addr even
; in "fast" mode, ISR calls fill this array with called vectors
logptr:
.word ; addr of next logentry to fill. 0 in "slow" mode
logbuf:
.end

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -36,6 +36,11 @@
#include "unibusdevice.hpp" // definition of class device_c
#include "cpu.hpp"
/* Adapter procs to Angelos CPU are not members of cpu_c calss
and need one global reference.
*/
static cpu_c *the_cpu = NULL;
cpu_c::cpu_c() :
unibusdevice_c() // super class constructor
{
@@ -46,62 +51,77 @@ cpu_c::cpu_c() :
default_base_addr = 0; // none
default_intr_vector = 0;
default_intr_level = 0;
priority_slot.value = 1 ;
dma_request.set_priority_slot(priority_slot.value);
// init parameters
runmode.value = false;
init.value = false;
// current CPU does not publish registers to the bus
// must be unibusdevice_c then!
register_count = 0;
memset(&bus, 0, sizeof(bus)) ;
memset(&ka11, 0, sizeof(ka11)) ;
ka11.bus = &bus ;
memset(&bus, 0, sizeof(bus));
memset(&ka11, 0, sizeof(ka11));
ka11.bus = &bus;
assert(the_cpu == NULL); // only one possible
the_cpu = this; // Singleton
}
cpu_c::~cpu_c() {
the_cpu = NULL;
}
bool cpu_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
if (!enabled.new_value) {
// HALT disabled CPU
runmode.value = false;
init.value = false;
}
}
return device_c::on_param_changed(param); // more actions (for enable)
}
extern "C" {
// functions to be used by Angelos CPU emulator
// Result: 1 = OK, 0 = bus timeout
int cpu_dato(unsigned addr, unsigned data) {
bool timeout;
mailbox->dma.words[0] = data;
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER , UNIBUS_CONTROL_DATO, addr, 1);
return !timeout;
uint16_t wordbuffer = (uint16_t) data;
unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATO, addr, &wordbuffer, 1);
return the_cpu->dma_request.success;
}
int cpu_datob(unsigned addr, unsigned data) {
uint16_t wordbuffer = (uint16_t) data;
// TODO DATOB als 1 byte-DMA !
bool timeout;
mailbox->dma.words[0] = data;
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATOB, addr, 1);
return !timeout;
unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATOB, addr, &wordbuffer, 1);
return the_cpu->dma_request.success;
}
int cpu_dati(unsigned addr, unsigned *data) {
bool timeout;
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATI, addr, 1);
*data = mailbox->dma.words[0];
return !timeout;
uint16_t wordbuffer;
unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATI, addr, &wordbuffer, 1);
*data = wordbuffer;
// printf("DATI; ba=%o, data=%o\n", addr, *data) ;
return the_cpu->dma_request.success;
}
}
// background worker.
// udpate LEDS, poll switches direct to register flipflops
void cpu_c::worker(void) {
void cpu_c::worker(unsigned instance) {
UNUSED(instance); // only one
timeout_c timeout;
bool nxm;
unsigned pc = 0;
unsigned dr = 0760102;
unsigned opcode = 0;
(void) opcode;
while (!worker_terminate) {
while (!workers_terminate) {
// run full speed!
timeout.wait_us(1);
@@ -115,11 +135,10 @@ void cpu_c::worker(void) {
if (init.value) {
// user wants CPU reset
reset(&ka11) ;
init.value = 0 ;
reset(&ka11);
init.value = 0;
}
#if 0
if (runmode.value) {
// simulate a fetch
@@ -155,11 +174,6 @@ void cpu_c::on_after_register_access(unibusdevice_register_t *device_reg,
UNUSED(unibus_control);
}
bool cpu_c::on_param_changed(parameter_c *param) {
UNUSED(param) ;
return true ;
}
void cpu_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
@@ -170,7 +184,7 @@ void cpu_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("demo_io_c::on_init()");
INFO("cpu::on_init()");
}
}

View File

@@ -21,7 +21,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23-nov-2018 JH created
23-nov-2018 JH created
*/
#ifndef _CPU_HPP_
#define _CPU_HPP_
@@ -44,26 +44,28 @@ private:
public:
cpu_c();
~cpu_c() ;
~cpu_c();
// CPU accesses memory actively
dma_request_c dma_request = dma_request_c(this);
bool on_param_changed(parameter_c *param) override; // must implement
parameter_bool_c runmode = parameter_bool_c(this, "run", "r",/*readonly*/
false, "1 = CPU running, 0 = halt");
parameter_bool_c init = parameter_bool_c(this, "init", "i",/*readonly*/
false, "1 = CPU initalizing");
struct Bus bus ; // UNIBU Sinterface of CPU
struct KA11 ka11 ; // Angelos CPU state
false, "1 = CPU initializing");
struct Bus bus; // UNIBU Sinterface of CPU
struct KA11 ka11; // Angelos CPU state
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
};

View File

@@ -355,7 +355,6 @@ step(KA11 *cpu)
INA(PC, cpu->ir);
PC += 2; /* don't increment on bus error! */
by = !!(cpu->ir&B15);
br = sxt(cpu->ir)<<1;
src = cpu->ir>>6 & 077;

View File

@@ -45,15 +45,14 @@ demo_io_c::demo_io_c() :
name.value = "DEMO_IO";
type_name.value = "demo_io_c";
log_label = "di";
default_base_addr = 0760100; // overwritten in install()?
default_intr_vector = 0;
default_intr_level = 0;
set_default_bus_params(0760100, 31, 0, 0); // base addr, intr-vector, intr level
// init parameters
switch_feedback.value = false ;
switch_feedback.value = false;
// controller has only 2 register
register_count = 2 ;
register_count = 2;
switch_reg = &(this->registers[0]); // @ base addr
strcpy(switch_reg->name, "SR"); // "Switches and Display"
@@ -95,13 +94,17 @@ demo_io_c::demo_io_c() :
demo_io_c::~demo_io_c() {
// close all gpio value files
unsigned i ;
for (i=0 ; i < 5 ; i++)
gpio_inputs[i].close() ;
for (i=0 ; i < 4 ; i++)
gpio_outputs[i].close() ;
unsigned i;
for (i = 0; i < 5; i++)
gpio_inputs[i].close();
for (i = 0; i < 4; i++)
gpio_outputs[i].close();
}
bool demo_io_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
/* helper: opens the control file for a gpio
* exports, programs directions, assigns stream
@@ -113,7 +116,7 @@ void demo_io_c::gpio_open(fstream& value_stream, bool is_input, unsigned gpio_nu
// 1. export pin, so it appears as .../gpio<nr>
char export_filename[80];
ofstream export_file ;
ofstream export_file;
sprintf(export_filename, "%s/export", gpio_class_path);
export_file.open(export_filename);
@@ -127,7 +130,7 @@ void demo_io_c::gpio_open(fstream& value_stream, bool is_input, unsigned gpio_nu
// 2. Now we have directory /sys/class/gpio<number>
// Set to input or output
char direction_filename[80];
ofstream direction_file ;
ofstream direction_file;
sprintf(direction_filename, "%s/gpio%d/direction", gpio_class_path, gpio_number);
direction_file.open(direction_filename);
if (!direction_file.is_open()) {
@@ -179,9 +182,10 @@ void demo_io_c::gpio_set_output(unsigned output_index, unsigned value) {
// background worker.
// udpate LEDS, poll switches direct to register flipflops
void demo_io_c::worker(void) {
void demo_io_c::worker(unsigned instance) {
UNUSED(instance); // only one
timeout_c timeout;
while (!worker_terminate) {
while (!workers_terminate) {
timeout.wait_ms(100);
unsigned i;
@@ -201,7 +205,7 @@ void demo_io_c::worker(void) {
// into /sys/class/gpio<n>/value pseudo files
// LED control from switches or UNIBUS "DR" register?
if (! switch_feedback.value)
if (!switch_feedback.value)
register_value = get_register_dato_value(display_reg);
for (i = 0; i < 4; i++) {
register_bitmask = (1 << i);
@@ -224,11 +228,6 @@ void demo_io_c::on_after_register_access(unibusdevice_register_t *device_reg,
UNUSED(unibus_control);
}
bool demo_io_c::on_param_changed(parameter_c *param) {
UNUSED(param) ;
return true ;
}
void demo_io_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}

View File

@@ -50,20 +50,20 @@ private:
public:
demo_io_c();
~demo_io_c() ;
~demo_io_c();
bool on_param_changed(parameter_c *param) override; // must implement
parameter_bool_c switch_feedback = parameter_bool_c(this, "switch_feedback", "sf",/*readonly*/
false, "1 = hard wire Switches to LEDs, PDP-11 can not set LEDs");
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
};

View File

@@ -1,128 +0,0 @@
/* demo_regs.cpp: sample UNIBUS controller with register 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
demo_regs:
a device to test the event and
implements 64 registers at start of IOpage
0760000.. 0760076
all registers are marked as "active":
DATI and DATO are routed via events into the controller logic
UNIBUS is stopped with long SSYN
Registers have no functions, are simple memory cells.
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
//#include <iostream>
#include "utils.hpp"
#include "logger.hpp"
#include "unibus.h"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp" // definition of class device_c
#include "demo_regs.hpp"
demo_regs_c::demo_regs_c() :
unibusdevice_c() // super class constructor
{
unsigned i;
// static config
name.value = "DEMO_REGS";
type_name.value = "demo_regs_c";
log_label = "dr";
default_base_addr = 0760000; // overwritten in install()
default_intr_vector = 0;
default_intr_level = 0;
register_count = 32; // up to 760076
// registers are "active": receive "on_after_register_access"
for (i = 0; i < this->register_count; i++) {
unibusdevice_register_t *reg = &(this->registers[i]);
sprintf(reg->name, "reg[0%03o]", i); // name is register offset
reg->active_on_dati = true; // controller state change on read
reg->active_on_dato = true; // writing changes controller state
reg->reset_value = 0;
reg->writable_bits = 0xffff; // all registers are memory cells
}
// dynamic state
access_count.value = 0;
}
// background worker.
// Just print a heart beat
void demo_regs_c::worker(void) {
timeout_c timeout;
while (!worker_terminate) {
timeout.wait_ms(1000);
cout << ".";
}
}
// 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 demo_regs_c::on_after_register_access(unibusdevice_register_t *device_reg,
uint8_t unibus_control) {
// emulate a plain memory cell: written values can be read unchanged
if (unibus_control == UNIBUS_CONTROL_DATI) {
}
if (unibus_control == UNIBUS_CONTROL_DATO)
set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__);
// this is also called for some DATIs, no action anyhow.
access_count.value++;
// DEBUG writes to disk & console ... measured delay up to 30ms !
//DEBUG(LL_DEBUG, LC_demo_regs, "[%6u] reg +%d @ %06o %s", accesscount, (int ) device_reg->index,
// device_reg->addr, unibus_c::control2text(unibus_control));
}
bool demo_regs_c::on_param_changed(parameter_c *param) {
UNUSED(param) ;
return true ;
}
void demo_regs_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
}
// UNIBUS INIT: clear all registers
void demo_regs_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("demo_regs_c::on_init()");
}
}

View File

@@ -0,0 +1,638 @@
/* 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 <assert.h>
#include <errno.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include "utils.hpp"
#include "gpios.hpp"
#include "logsource.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp" // definition of class device_c
#include "unibus.h"
#include "dl11w.hpp"
#include "rs232.hpp"
#include "rs232adapter.hpp"
//-------------------------------------------------
slu_c::slu_c() :
unibusdevice_c() {
set_workers_count(2); // receiver and transmitte have own threads
//ip_host.value = IP_HOST; // not used
//ip_port.value = IP_PORT; // not used
// static config
name.value = "DL11";
type_name.value = "slu_c";
log_label = "slu";
break_enable.value = 1; // SW4-1 default ON
error_bits_enable.value = 1; // SE4-7 default ON
// SLU has 2 Interrupt vectors: base = RCV, base+= XMT
// put in slot 1, closest to CPU
set_default_bus_params(SLU_ADDR, SLU_SLOT, SLU_VECTOR, SLU_LEVEL); // base addr, intr-vector, intr level
rcvintr_request.set_priority_slot(default_priority_slot);
rcvintr_request.set_level(default_intr_level);
rcvintr_request.set_vector(default_intr_vector);
// XMT INTR: lower priority->netx slot, and next vector
xmtintr_request.set_priority_slot(default_priority_slot + 1);
xmtintr_request.set_level(default_intr_level);
xmtintr_request.set_vector(default_intr_vector + 4);
// 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 = false;
reg_rcsr->active_on_dato = true;
reg_rcsr->reset_value = 0;
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;
reg_rbuf->active_on_dato = true; // required for "active on dati""
reg_rbuf->reset_value = 0;
reg_rbuf->writable_bits = 0x00;
reg_xcsr = &(this->registers[slu_idx_xcsr]); // @ base addr
strcpy(reg_xcsr->name, "XCSR"); // Transmitter Status Register
reg_xcsr->active_on_dati = false;
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 = false; // no controller state change
reg_xbuf->active_on_dato = true;
reg_xbuf->reset_value = 0;
reg_xbuf->writable_bits = 0xff;
// initialize serial format
serialport.value = "ttyS2"; // labeled "UART2" on PCB
baudrate.value = 9600;
mode.value = "8N1";
rs232adapter.rs232 = &rs232;
}
slu_c::~slu_c() {
}
bool slu_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
if (enabled.new_value) {
// enable SLU: setup COM serial port
// setup for BREAK and parity evaluation
rs232adapter.rcv_termios_error_encoding = true;
if (rs232.OpenComport(serialport.value.c_str(), baudrate.value, mode.value.c_str(),
true)) {
ERROR("Can not open serial port %s", serialport.value.c_str());
return false; // reject "enable"
}
// lock serial port and settings
serialport.readonly = true;
baudrate.readonly = true;
mode.readonly = true;
INFO("Serial port %s opened", serialport.value.c_str());
char buff[256];
sprintf(buff, "Serial port %s opened\n\r", serialport.value.c_str());
rs232.cputs(buff);
} else {
// disable SLU
rs232.CloseComport();
// unlock serial port and settings
serialport.readonly = false;
baudrate.readonly = false;
mode.readonly = false;
INFO("Serial port %s closed", serialport.value.c_str());
}
}
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
//--------------------------------------------
// calc static INTR condition level.
// Change of that condition calculated by intr_request_c.is_condition_raised()
bool slu_c::get_rcv_intr_level(void) {
return rcv_done && rcv_intr_enable;
}
// Update RCSR and optionally generate INTR
void slu_c::set_rcsr_dati_value_and_INTR(void) {
uint16_t val = (rcv_active ? RCSR_RCVR_ACT : 0) | (rcv_done ? RCSR_RCVR_DONE : 0)
| (rcv_intr_enable ? RCSR_RCVR_INT_ENB : 0);
switch (rcvintr_request.edge_detect(get_rcv_intr_level())) {
case intr_request_c::INTERRUPT_EDGE_RAISING:
// set register atomically with INTR, if INTR not blocked
unibusadapter->INTR(rcvintr_request, reg_rcsr, val);
break;
case intr_request_c::INTERRUPT_EDGE_FALLING:
// BR4 is tied to monitor and enable, so raised INTRs may get canceled
unibusadapter->cancel_INTR(rcvintr_request);
set_register_dati_value(reg_rcsr, val, __func__);
break;
default:
set_register_dati_value(reg_rcsr, val, __func__);
}
}
// PDP-11 writes into RCSR
void slu_c::eval_rcsr_dato_value(void) {
uint16_t val = get_register_dato_value(reg_rcsr);
rcv_intr_enable = val & RCSR_RCVR_INT_ENB ? 1 : 0;
rcv_rdr_enb = val & RCSR_RDR_ENB ? 1 : 0;
if (rcv_rdr_enb)
rcv_done = 0; // raising edge clears rcv_done
// if rcvr_done and int enable goes high: INTR
set_rcsr_dati_value_and_INTR();
}
// Update RBUF, readonly
void slu_c::set_rbuf_dati_value(void) {
uint16_t val = 0;
if (error_bits_enable.value) {
val = (rcv_or_err ? RBUF_OR_ERR : 0) | (rcv_fr_err ? RBUF_FR_ERR : 0)
| (rcv_p_err ? RBUF_P_ERR : 0);
if (val) // set general error flag
val |= RBUF_ERROR;
}
val |= rcv_buffer; // received char in bits 7..0
set_register_dati_value(reg_rbuf, val, __func__);
}
bool slu_c::get_xmt_intr_level() {
return xmt_ready && xmt_intr_enable;
}
// Update Transmit Status Register XCSR and optionally generate INTR
void slu_c::set_xcsr_dati_value_and_INTR(void) {
uint16_t val = (xmt_ready ? XCSR_XMIT_RDY : 0) | (xmt_intr_enable ? XCSR_XMIT_INT_ENB : 0)
| (xmt_maint ? XCSR_MAINT : 0) | (xmt_break ? XCSR_BREAK : 0);
switch (xmtintr_request.edge_detect(get_xmt_intr_level())) {
case intr_request_c::INTERRUPT_EDGE_RAISING:
// set register atomically with INTR, if INTR not blocked
unibusadapter->INTR(xmtintr_request, reg_xcsr, val);
break;
case intr_request_c::INTERRUPT_EDGE_FALLING:
// BR4 is tied to monitor and enable, so raised INTRs may get canceled
unibusadapter->cancel_INTR(xmtintr_request);
set_register_dati_value(reg_xcsr, val, __func__);
break;
default:
set_register_dati_value(reg_xcsr, val, __func__);
}
}
void slu_c::eval_xcsr_dato_value(void) {
uint16_t val = get_register_dato_value(reg_xcsr);
bool old_break = xmt_break;
xmt_intr_enable = val & XCSR_XMIT_INT_ENB ? 1 : 0;
xmt_maint = val & XCSR_MAINT ? 1 : 0;
xmt_break = val & XCSR_BREAK ? 1 : 0;
// if xmt_ready and int enable goes high: INTR
set_xcsr_dati_value_and_INTR();
if (old_break != xmt_break) {
// re-evaluate break state on bit change
if (break_enable.value)
rs232.SetBreak(xmt_break);
else
rs232.SetBreak(0);
}
}
void slu_c::eval_xbuf_dato_value(void) {
// transmit data buffer contains only the character in bits 7..0
xmt_buffer = get_register_dato_value(reg_xbuf) & 0xff;
}
// 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__);
if (unibusadapter->line_INIT)
return; // do nothing wile reset
switch (device_reg->index) {
case slu_idx_rcsr:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write into RCSR
pthread_mutex_lock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses
eval_rcsr_dato_value(); // may generate INTR
set_rcsr_dati_value_and_INTR();
// ignore reader enable
pthread_mutex_unlock(&on_after_rcv_register_access_mutex);
}
break;
case slu_idx_rbuf: { // DATI/DATO: is read only, but write also clears "rcvr_done"
// signal data has been read from bus
pthread_mutex_lock(&on_after_rcv_register_access_mutex);
rcv_done = 0;
set_rcsr_dati_value_and_INTR();
pthread_mutex_unlock(&on_after_rcv_register_access_mutex);
}
break;
case slu_idx_xcsr:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
pthread_mutex_lock(&on_after_xmt_register_access_mutex);
eval_xcsr_dato_value(); // may trigger INTR
set_xcsr_dati_value_and_INTR();
pthread_mutex_unlock(&on_after_xmt_register_access_mutex);
}
break;
case slu_idx_xbuf:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write into XBUF
pthread_mutex_lock(&on_after_xmt_register_access_mutex);
eval_xbuf_dato_value();
xmt_ready = 0; // signal worker: xmt_data pending
set_xcsr_dati_value_and_INTR();
// on_after_register_access_cond used for xmt worker
pthread_cond_signal(&on_after_xmt_register_access_cond);
pthread_mutex_unlock(&on_after_xmt_register_access_mutex);
}
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();
rcv_active = 0;
rcv_done = 0;
rcv_intr_enable = 0;
rcv_or_err = 0;
rcv_fr_err = 0;
rcv_p_err = 0;
rcv_buffer = 0;
xmt_ready = 1;
xmt_intr_enable = 0;
xmt_maint = 0;
xmt_break = 0;
rcvintr_request.edge_detect_reset();
xmtintr_request.edge_detect_reset();
// INFO("slu_c::on_init()");
}
}
// background worker.
void slu_c::worker_rcv(void) {
timeout_c timeout;
int n;
unsigned char buffer[BUFLEN + 1];
// poll with frequency > baudrate, to see single bits
//unsigned poll_periods_us = 1000000 / baudrate.value;
/* Receiver not time critical? UARTS are buffering
So if thread is swapped out and back a burst of characters appear.
-> Wait after each character for transfer time before polling
RS232 again.
*/
unsigned poll_periods_us = (rs232.CharTransmissionTime_us * 9) / 10;
// poll a bit faster to be ahead of char stream.
// don't oversample: PDP-11 must process char in that time
// worker_init_realtime_priority(rt_device);
while (!workers_terminate) {
timeout.wait_us(poll_periods_us);
if (unibusadapter->line_INIT)
continue; // do nothing while reset
// "query
// rcv_active: can only be set by polling the UART input GPIO pin?
// at the moments, it is only sent on maintenance loopback xmt
/* read serial data, if any */
if (rs232adapter.byte_rcv_poll(buffer)) {
pthread_mutex_lock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses
rcv_or_err = rcv_fr_err = rcv_p_err = 0;
if (rcv_done) // not yet cleared? overrun!
rcv_or_err = 1;
if (buffer[0] == 0xff) {
/* How to receive framing and parity errors: see termios(3)
If IGNPAR=0, PARMRK=1: error on <char> received as \377 \0 <char>
\377 received as \377 \377
*/
n = rs232adapter.byte_rcv_poll(buffer);
assert(n); // next char after 0xff escape immediately available
if (buffer[0] == 0) { // error flags
rcv_fr_err = rcv_p_err = 1;
n = rs232adapter.byte_rcv_poll(buffer);
assert(n); // next char after 0xff 0 seq is data"
rcv_buffer = buffer[0];
} else if (buffer[0] == 0xff) { // enocoded 0xff
rcv_buffer = 0xff;
} else {
WARNING("Received 0xff <stray> seqeuence");
rcv_buffer = buffer[0];
}
} else
// received non escaped data byte
rcv_buffer = buffer[0];
rcv_done = 1;
rcv_active = 0;
set_rbuf_dati_value();
set_rcsr_dati_value_and_INTR(); // INTR!
pthread_mutex_unlock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses
}
}
}
void slu_c::worker_xmt(void) {
timeout_c timeout;
assert(!pthread_mutex_lock(&on_after_register_access_mutex));
// Transmitter not time critical
// worker_init_realtime_priority(rt_device);
while (!workers_terminate) {
// 1. wait for xmt signal
int res = pthread_cond_wait(&on_after_xmt_register_access_cond,
&on_after_xmt_register_access_mutex);
// on_after_xmt_register_access_mutex remains locked all the time
if (res != 0) {
ERROR("SLU::worker_xmt() pthread_cond_wait = %d = %s>", res, strerror(res));
continue;
}
// 2. transmit
rs232adapter.byte_xmt_send(xmt_buffer);
xmt_ready = 0;
set_xcsr_dati_value_and_INTR();
if (xmt_maint) { // loop back: simulate data byte coming in
pthread_mutex_lock(&on_after_rcv_register_access_mutex);
rcv_active = 1;
set_rcsr_dati_value_and_INTR();
pthread_mutex_unlock(&on_after_rcv_register_access_mutex);
}
// 3. wait for data byte being shifted out
pthread_mutex_unlock(&on_after_xmt_register_access_mutex);
timeout.wait_us(rs232.CharTransmissionTime_us);
pthread_mutex_lock(&on_after_xmt_register_access_mutex);
if (xmt_maint)
// put sent byte into rcv buffer, receiver will poll it
rs232adapter.byte_loopback(xmt_buffer);
xmt_ready = 1;
set_xcsr_dati_value_and_INTR();
// has rcv or xmt interrupt priority on maintennace loop back
}
assert(!pthread_mutex_unlock(&on_after_xmt_register_access_mutex));
}
void slu_c::worker(unsigned instance) {
// 2 parallel worker() instances: 0 and 1
if (instance == 0)
worker_rcv();
else
worker_xmt();
}
//--------------------------------------------------------------------------------------------------
ltc_c::ltc_c() :
unibusdevice_c() // super class constructor
{
// static config
name.value = "KW11";
type_name.value = "ltc_c";
log_label = "ltc";
// slot = 3:
set_default_bus_params(LTC_ADDR, LTC_SLOT, LTC_VECTOR, LTC_LEVEL); // base addr, intr-vector, intr level
intr_request.set_priority_slot(default_priority_slot);
intr_request.set_level(default_intr_level);
intr_request.set_vector(default_intr_vector);
// controller has only one register
register_count = 1;
reg_lks = &(this->registers[0]); // @ base addr
strcpy(reg_lks->name, "LKS"); // Line Clock Status Register
reg_lks->active_on_dati = false; // status polled by CPU, not active
reg_lks->active_on_dato = true;
reg_lks->reset_value = 0;
reg_lks->writable_bits = LKS_INT_ENB; // interrupt enable
// init parameters
frequency.value = 50;
ltc_enable.value = true;
// init controller state
intr_enable = 0;
intr_monitor = 0;
}
ltc_c::~ltc_c() {
}
bool ltc_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic here
if (param == &frequency) {
// accept only these
return (frequency.new_value == 50 || frequency.new_value == 60);
} else
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// calc static INTR condition level.
// Change of that condition calculated by intr_request_c.is_condition_raised()
// on raising edge.
bool ltc_c::get_intr_signal_level() {
return intr_monitor && intr_enable;
}
// set status register, and optionally generate INTR
// intr_raise: if inactive->active transition of interrupt condition detected.
void ltc_c::set_lks_dati_value_and_INTR(void) {
uint16_t val = (intr_monitor ? LKS_INT_MON : 0) | (intr_enable ? LKS_INT_ENB : 0);
switch (intr_request.edge_detect(get_intr_signal_level())) {
case intr_request_c::INTERRUPT_EDGE_RAISING:
// set register atomically with INTR, if INTR not blocked
unibusadapter->INTR(intr_request, reg_lks, val);
break;
case intr_request_c::INTERRUPT_EDGE_FALLING:
// BR6 is tied to monitor and enable, so raised INTRs may get canceled
unibusadapter->cancel_INTR(intr_request);
set_register_dati_value(reg_lks, val, __func__);
break;
default:
set_register_dati_value(reg_lks, val, __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) {
pthread_mutex_lock(&on_after_register_access_mutex);
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 0: // LKS
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
intr_enable = !!(reg_lks->active_dato_flipflops & LKS_INT_ENB);
intr_monitor = !!(reg_lks->active_dato_flipflops & LKS_INT_MON);
set_lks_dati_value_and_INTR();
}
break;
default:
break;
}
pthread_mutex_unlock(&on_after_register_access_mutex);
}
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();
intr_enable = 0;
intr_monitor = 1;
intr_request.edge_detect_reset();
// INFO("ltc_c::on_init()");
}
}
/* background worker.
Freqnecy of clock signal edges is tied to system time, not to "wait" periods
This worker may get delayed arbitray amount of time (as every thread),
lost edges are compensated
*/
void ltc_c::worker(unsigned instance) {
UNUSED(instance); // only one
timeout_c global_time;
timeout_c timeout;
int64_t global_next_edge_ns;
INFO("KW11 time resolution is < %u us",
(unsigned )(global_time.get_resolution_ns() / 1000));
global_time.start_ns(0);
global_next_edge_ns = global_time.elapsed_ns();
uint64_t global_edge_count = 0;
while (!workers_terminate) {
// signal egde period may change if 50/60 Hz is changed
uint64_t edge_period_ns = BILLION / (2 * frequency.value);
uint64_t wait_ns;
// overdue_ns: time which signal edge is too late
int64_t overdue_ns = (int64_t) global_time.elapsed_ns() - global_next_edge_ns;
// INFO does not work on 64 ints
// printf("elapsed [ms] =%u, overdue [us] =%u\n", (unsigned) global_time.elapsed_ms(), (unsigned) overdue_ns/1000) ;
// if overdue_ns positive, next signal edge should have occured
if (overdue_ns < 0) {
wait_ns = -overdue_ns; // wait until next edge time reached
} else {
// time for next signal edge reached
if (ltc_enable.value) {
global_edge_count++;
clock_signal = !clock_signal; // square wave
if (clock_signal) {
intr_monitor = 1;
pthread_mutex_lock(&on_after_register_access_mutex);
set_lks_dati_value_and_INTR();
pthread_mutex_unlock(&on_after_register_access_mutex);
}
} else
// clock disconnected
clock_signal = 0;
// time of next signal edge
global_next_edge_ns += edge_period_ns;
// overdue_ns now time which next signal edge is too late
overdue_ns -= edge_period_ns;
if (overdue_ns < 0)
// next edge now in future: wait exact
wait_ns = -overdue_ns;
else
// next edge still in past:
// wait shorter than signal edge period to keep up slowly
wait_ns = edge_period_ns / 2;
//if ((global_edge_count % 100) == 0)
// INFO("LTC: %u secs by edges", (unsigned)(global_edge_count/100) ) ;
}
timeout.wait_ns(wait_ns);
}
}

View File

@@ -0,0 +1,243 @@
/* DL11W.hpp: 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 DL11-W device
*/
#ifndef _DL11W_HPP_
#define _DL11W_HPP_
#include <fstream>
using namespace std;
#include "utils.hpp"
#include "unibusdevice.hpp"
#include "parameter.hpp"
#include "rs232.hpp"
#include "rs232adapter.hpp"
// socket console settings
//#define IP_PORT 5001
//#define IP_HOST "localhost"
// bus properties
#define DL11A 1
#if DL11A // console (teletype keyboard & printer)
#define SLU_ADDR 0777560
#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT
#define SLU_LEVEL 04
#define SLU_VECTOR 060 // RCV +0, XMT +4
#elif DL11B // paper tape punch and reader
#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT
#define SLU_ADDR 0777550
#define SLU_LEVEL 04
#define SLU_VECTOR 070
#else // other serial device
#define SLU_ADDR 0776500
#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT
#define SLU_LEVEL 04
#define SLU_VECTOR 0300
#endif
#define LTC_ADDR 0777546
#define LTC_SLOT (SLU_SLOT+2)
#define LTC_LEVEL 06
#define LTC_VECTOR 0100
// global text buffer size for hostname etc
#define BUFLEN 32
// register bit definitions
#define RCSR_RCVR_ACT 0004000
#define RCSR_RCVR_DONE 0000200
#define RCSR_RCVR_INT_ENB 0000100
#define RCSR_RDR_ENB 0000001
#define RBUF_ERROR 0100000
#define RBUF_OR_ERR 0040000
#define RBUF_FR_ERR 0020000
#define RBUF_P_ERR 0010000
#define XCSR_XMIT_RDY 0000200
#define XCSR_XMIT_INT_ENB 0000100
#define XCSR_MAINT 0000004
#define XCSR_BREAK 0000001
#define LKS_INT_ENB 0000100
#define LKS_INT_MON 0000200
// background task sleep times
#define SLU_MSRATE_MS 10
#define LTC_MSRATE_MS 50
// unibus register indices
enum slu_reg_index {
slu_idx_rcsr = 0, slu_idx_rbuf, slu_idx_xcsr, slu_idx_xbuf, slu_idx_count,
};
// ------------------------------------------ SLU -----------------------------
class slu_c: public unibusdevice_c {
private:
rs232_c rs232; /// COM port interface
public:
rs232adapter_c rs232adapter; /// stream router
private:
unibusdevice_register_t *reg_rcsr;
unibusdevice_register_t *reg_rbuf;
unibusdevice_register_t *reg_xcsr;
unibusdevice_register_t *reg_xbuf;
// two interrupts of same level, need slot and slot+1
intr_request_c rcvintr_request = intr_request_c(this);
intr_request_c xmtintr_request = intr_request_c(this);
/*** SLU is infact 2 independend devices: RCV and XMT ***/
pthread_cond_t on_after_rcv_register_access_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t on_after_rcv_register_access_mutex = PTHREAD_MUTEX_INITIALIZER;
// bits in registers
bool rcv_active; /// while a char is receive ... not available
bool rcv_done; // char received. INTR. cleared by rdr_enable, access to rbuf, init
bool rcv_overrun;bool rcv_intr_enable; // receiver interrupt enabled
bool rcv_or_err; // receiver overrun: rcv_done 1 on receive
bool rcv_fr_err; // framing error. high on received BREAK
bool rcv_p_err; // parity error
uint8_t rcv_buffer;bool rcv_rdr_enb; // reader enable. Cleared by receive or init
pthread_cond_t on_after_xmt_register_access_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t on_after_xmt_register_access_mutex = PTHREAD_MUTEX_INITIALIZER;bool xmt_ready; // transmitter ready. INTR, cleared on XBUF access
bool xmt_intr_enable; // receiver interrupt enabled
bool xmt_maint; // set 1 for local loop back
bool xmt_break; // transmit continuous break
uint8_t xmt_buffer;
// convert between register ansd state variables
bool get_rcv_intr_level(void);bool get_xmt_intr_level(void);
void set_rcsr_dati_value_and_INTR(void);
void eval_rcsr_dato_value(void);
void set_rbuf_dati_value(void);
void set_xcsr_dati_value_and_INTR(void);
void eval_xcsr_dato_value(void);
void eval_xbuf_dato_value(void);
public:
slu_c();
~slu_c();
//parameter_string_c ip_host = parameter_string_c( this, "SLU socket IP host", "host", /*readonly*/ false, "ip hostname");
//parameter_unsigned_c ip_port = parameter_unsigned_c(this, "SLU socket IP serialport", "serialport", /*readonly*/ false, "", "%d", "ip serialport", 32, 10);
parameter_string_c serialport = parameter_string_c(this, "serialport", "p", /*readonly*/
false, "Linux serial port: \"ttyS1\" or \"ttyS2\"");
parameter_unsigned_c baudrate = parameter_unsigned_c(this, "baudrate", "b", /*readonly*/
false, "", "%d", "Baudrate: 110, 300, ... 38400", 38400, 10);
// 40kbaud -> 25us bit polling period needed
parameter_string_c mode = parameter_string_c(this, "mode", "m", /*readonly*/false,
"Mode: 8N1, 7E1, ... ");
parameter_bool_c error_bits_enable = parameter_bool_c(this, "errorbits", "eb", /*readonly*/
false, "Enable error bits (M7856 SW4-7)");
parameter_bool_c break_enable = parameter_bool_c(this, "break", "b", /*readonly*/false,
"Enable BREAK transmission (M7856 SW4-1)");
#ifdef USED
// @David: duplicating device registers as parameters is not necessary ...
// they can be seen with "exam" anyhow.
parameter_unsigned_c rcsr = parameter_unsigned_c(this, "Receiver Status Register", "rcsr", /*readonly*/
false, "", "%o", "Internal state", 32, 8);
parameter_unsigned_c rbuf = parameter_unsigned_c(this, "Receiver Buffer Register", "rbuf", /*readonly*/
false, "", "%o", "Internal state", 32, 8);
parameter_unsigned_c xcsr = parameter_unsigned_c(this, "Transmitter Status Register",
"xcsr", /*readonly*/false, "", "%o", "Internal state", 32, 8);
parameter_unsigned_c xbuf = parameter_unsigned_c(this, "Transmitter Buffer Register",
"xbuf", /*readonly*/false, "", "%o", "Internal state", 32, 8);
parameter_bool_c xmit_interrupt_enable = parameter_bool_c(this, "XMIT interrupt enable",
"xie",/*readonly*/false, "1 = enable interrupt");
parameter_bool_c rcvr_interrupt_enable = parameter_bool_c(this, "RCVR interrupt enable",
"rie",/*readonly*/false, "1 = enable interrupt");
parameter_bool_c slu_maint = parameter_bool_c(this, "XCSR Maintenance", "maint",/*readonly*/
false, "1 = enable Maintenance mode enabled");
parameter_bool_c rdr_enable = parameter_bool_c(this, "RDR enable", "rdre",/*readonly*/false,
"1 = enable reader enable");
#endif
// background worker function
void worker(unsigned instance) override;
void worker_rcv(void);
void worker_xmt(void);
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
};
//-------------------------------------------- LTC -------------------------------------
class ltc_c: public unibusdevice_c {
private:
unibusdevice_register_t *reg_lks;
// KW11 has one interrupt
intr_request_c intr_request = intr_request_c(this);
bool clock_signal; // value of 50Hz square wave signal
bool intr_enable; // interrupt enable, LKS bit 6
bool intr_monitor ; // LKS bit 7
bool get_intr_signal_level(void);
void set_lks_dati_value_and_INTR(void);
public:
ltc_c();
~ltc_c();
parameter_unsigned_c frequency = parameter_unsigned_c(this, "Line clock frequency", "freq", /*readonly*/
false, "", "%d", "50/60 Hz", 32, 10);
parameter_bool_c ltc_enable = parameter_bool_c(this, "LTC input enable", "ltc",/*readonly*/
false, "1 = enable update of LKS by LTC Input");
// background worker function
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
};
#endif

View File

@@ -1,19 +1,19 @@
/*
mscp_drive.cpp: Implementation of MSCP disks.
mscp_drive.cpp: Implementation of MSCP disks.
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
This provides the logic for reads and writes to the data and RCT space
for a given drive, as well as configuration for different standard DEC
drive types.
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
Disk data is backed by an image file on disk. RCT data exists only in
memory and is not saved -- it is provided to satisfy software that
expects the RCT area to exist. Since no bad sectors will ever actually
exist, the RCT area has no real purpose, so it is ephemeral in this
implementation.
*/
This provides the logic for reads and writes to the data and RCT space
for a given drive, as well as configuration for different standard DEC
drive types.
Disk data is backed by an image file on disk. RCT data exists only in
memory and is not saved -- it is provided to satisfy software that
expects the RCT area to exist. Since no bad sectors will ever actually
exist, the RCT area has no real purpose, so it is ephemeral in this
implementation.
*/
#include <assert.h>
#include <memory>
@@ -25,26 +25,44 @@ using namespace std;
#include "mscp_drive.hpp"
#include "mscp_server.hpp"
mscp_drive_c::mscp_drive_c(
storagecontroller_c *controller,
uint32_t driveNumber) :
storagedrive_c(controller),
_useImageSize(false)
{
log_label = "MSCPD";
SetDriveType("RA81");
SetOffline();
mscp_drive_c::mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber) :
storagedrive_c(controller), _useImageSize(false) {
set_workers_count(0) ; // needs no worker()
log_label = "MSCPD";
SetDriveType("RA81");
SetOffline();
// Calculate the unit's ID:
_unitDeviceNumber = driveNumber + 1;
// Calculate the unit's ID:
_unitDeviceNumber = driveNumber + 1;
}
mscp_drive_c::~mscp_drive_c()
{
if (file_is_open())
{
file_close();
}
mscp_drive_c::~mscp_drive_c() {
if (file_is_open()) {
file_close();
}
}
// on_param_changed():
// Handles configuration parameter changes.
bool mscp_drive_c::on_param_changed(parameter_c *param) {
// no own "enable" logic
if (&type_name == param) {
return SetDriveType(type_name.new_value.c_str());
} else if (&image_filepath == param) {
// Try to open the image file.
if (file_open(image_filepath.new_value, true)) {
image_filepath.value = image_filepath.new_value;
UpdateCapacity();
return true;
}
// TODO: if file is a nonstandard size?
} else if (&use_image_size == param) {
_useImageSize = use_image_size.new_value;
use_image_size.value = use_image_size.new_value;
UpdateCapacity();
return true;
}
return device_c::on_param_changed(param); // more actions (for enable)false;
}
//
@@ -52,12 +70,11 @@ mscp_drive_c::~mscp_drive_c()
// Returns the size, in bytes, of a single block on this drive.
// This is either 512 or 576 bytes.
//
uint32_t mscp_drive_c::GetBlockSize()
{
//
// For the time being this is always 512 bytes.
//
return 512;
uint32_t mscp_drive_c::GetBlockSize() {
//
// For the time being this is always 512 bytes.
//
return 512;
}
//
@@ -65,65 +82,56 @@ uint32_t mscp_drive_c::GetBlockSize()
// Get the size of the data space (not including RCT area) of this
// drive, in blocks.
//
uint32_t mscp_drive_c::GetBlockCount()
{
if (_useImageSize)
{
// Return the image size / Block size (rounding down).
return file_size() / GetBlockSize();
}
else
{
//
// Use the size defined by the drive type.
//
return _driveInfo.BlockCount;
}
uint32_t mscp_drive_c::GetBlockCount() {
if (_useImageSize) {
// Return the image size / Block size (rounding down).
return file_size() / GetBlockSize();
} else {
//
// Use the size defined by the drive type.
//
return _driveInfo.BlockCount;
}
}
//
// GetRCTBlockCount():
// Returns the total size of the RCT area in blocks.
//
uint32_t mscp_drive_c::GetRCTBlockCount()
{
return _driveInfo.RCTSize * GetRCTCopies();
uint32_t mscp_drive_c::GetRCTBlockCount() {
return _driveInfo.RCTSize * GetRCTCopies();
}
//
// GetMediaID():
// Returns the media ID specific to this drive's type.
//
uint32_t mscp_drive_c::GetMediaID()
{
return _driveInfo.MediaID;
uint32_t mscp_drive_c::GetMediaID() {
return _driveInfo.MediaID;
}
//
// GetDeviceNumber():
// Returns the unique device number for this drive.
//
uint32_t mscp_drive_c::GetDeviceNumber()
{
return _unitDeviceNumber;
uint32_t mscp_drive_c::GetDeviceNumber() {
return _unitDeviceNumber;
}
//
// GetClassModel():
// Returns the class and model information for this drive.
//
uint16_t mscp_drive_c::GetClassModel()
{
return _unitClassModel;
uint16_t mscp_drive_c::GetClassModel() {
return _unitClassModel;
}
//
// GetRCTSize():
// Returns the size of one copy of the RCT.
//
uint16_t mscp_drive_c::GetRCTSize()
{
return _driveInfo.RCTSize;
uint16_t mscp_drive_c::GetRCTSize() {
return _driveInfo.RCTSize;
}
//
@@ -131,9 +139,8 @@ uint16_t mscp_drive_c::GetRCTSize()
// Returns the number of replacement blocks per track for
// this drive.
//
uint8_t mscp_drive_c::GetRBNs()
{
return 0;
uint8_t mscp_drive_c::GetRBNs() {
return 0;
}
//
@@ -141,9 +148,8 @@ uint8_t mscp_drive_c::GetRBNs()
// Returns the number of copies of the RCT present in the RCT
// area.
//
uint8_t mscp_drive_c::GetRCTCopies()
{
return 1;
uint8_t mscp_drive_c::GetRCTCopies() {
return 1;
}
//
@@ -151,9 +157,8 @@ uint8_t mscp_drive_c::GetRCTCopies()
// Indicates whether this drive is available (i.e. has an image
// assigned to it and can thus be used by the controller.)
//
bool mscp_drive_c::IsAvailable()
{
return file_is_open();
bool mscp_drive_c::IsAvailable() {
return file_is_open();
}
//
@@ -161,51 +166,41 @@ bool mscp_drive_c::IsAvailable()
// Indicates whether this drive has been placed into an Online
// state (for example by the ONLINE command).
//
bool mscp_drive_c::IsOnline()
{
return _online;
bool mscp_drive_c::IsOnline() {
return _online;
}
//
// SetOnline():
// Brings the drive online.
//
void mscp_drive_c::SetOnline()
{
_online = true;
void mscp_drive_c::SetOnline() {
_online = true;
//
// Once online, the drive's type and image cannot be changed until
// the drive is offline.
//
// type_name.readonly = true;
// image_filepath.readonly = true;
//
// Once online, the drive's type and image cannot be changed until
// the drive is offline.
//
// type_name.readonly = true;
// image_filepath.readonly = true;
}
//
// SetOffline():
// Takes the drive offline.
//
void mscp_drive_c::SetOffline()
{
_online = false;
type_name.readonly = false;
image_filepath.readonly = false;
void mscp_drive_c::SetOffline() {
_online = false;
type_name.readonly = false;
image_filepath.readonly = false;
}
//
// Writes the specified number of bytes from the provided buffer,
// starting at the specified logical block.
//
void mscp_drive_c::Write(
uint32_t blockNumber,
size_t lengthInBytes,
uint8_t* buffer)
{
file_write(
buffer,
blockNumber * GetBlockSize(),
lengthInBytes);
void mscp_drive_c::Write(uint32_t blockNumber, size_t lengthInBytes, uint8_t* buffer) {
file_write(buffer, blockNumber * GetBlockSize(), lengthInBytes);
}
//
@@ -213,20 +208,14 @@ void mscp_drive_c::Write(
// block. Returns a pointer to a buffer containing the data read.
// Caller is responsible for freeing this buffer.
//
uint8_t* mscp_drive_c::Read(
uint32_t blockNumber,
size_t lengthInBytes)
{
uint8_t* buffer = new uint8_t[lengthInBytes];
uint8_t* mscp_drive_c::Read(uint32_t blockNumber, size_t lengthInBytes) {
uint8_t* buffer = new uint8_t[lengthInBytes];
assert(nullptr != buffer);
assert(nullptr != buffer);
file_read(
buffer,
blockNumber * GetBlockSize(),
lengthInBytes);
file_read(buffer, blockNumber * GetBlockSize(), lengthInBytes);
return buffer;
return buffer;
}
//
@@ -234,16 +223,11 @@ uint8_t* mscp_drive_c::Read(
// RCT area at the specified RCT block. Buffer must be at least as large
// as the disk's block size.
//
void mscp_drive_c::WriteRCTBlock(
uint32_t rctBlockNumber,
uint8_t* buffer)
{
assert (rctBlockNumber < GetRCTBlockCount());
void mscp_drive_c::WriteRCTBlock(uint32_t rctBlockNumber, uint8_t* buffer) {
assert(rctBlockNumber < GetRCTBlockCount());
memcpy(
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
reinterpret_cast<void *>(buffer),
GetBlockSize());
memcpy(reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
reinterpret_cast<void *>(buffer), GetBlockSize());
}
//
@@ -251,84 +235,42 @@ void mscp_drive_c::WriteRCTBlock(
// block offset). Returns a pointer to a buffer containing the data read.
// Caller is responsible for freeing this buffer.
//
uint8_t* mscp_drive_c::ReadRCTBlock(
uint32_t rctBlockNumber)
{
assert (rctBlockNumber < GetRCTBlockCount());
uint8_t* mscp_drive_c::ReadRCTBlock(uint32_t rctBlockNumber) {
assert(rctBlockNumber < GetRCTBlockCount());
uint8_t* buffer = new uint8_t[GetBlockSize()];
assert (nullptr != buffer);
uint8_t* buffer = new uint8_t[GetBlockSize()];
assert(nullptr != buffer);
memcpy(
reinterpret_cast<void *>(buffer),
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
GetBlockSize());
return buffer;
}
memcpy(reinterpret_cast<void *>(buffer),
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
GetBlockSize());
return buffer;
}
//
// UpdateCapacity():
// Updates the capacity parameter of the drive based on the block count and block size.
//
void mscp_drive_c::UpdateCapacity()
{
capacity.value =
GetBlockCount() * GetBlockSize();
void mscp_drive_c::UpdateCapacity() {
capacity.value = GetBlockCount() * GetBlockSize();
}
//
// UpdateMetadata():
// Updates the Unit Class / Model info and RCT area based on the selected drive type.
//
void mscp_drive_c::UpdateMetadata()
{
_unitClassModel = 0x0200 | _driveInfo.Model;
void mscp_drive_c::UpdateMetadata() {
_unitClassModel = 0x0200 | _driveInfo.Model;
// Initialize the RCT area
size_t rctSize = _driveInfo.RCTSize * GetBlockSize();
_rctData.reset(new uint8_t[rctSize]);
assert(_rctData != nullptr);
memset(reinterpret_cast<void *>(_rctData.get()), 0, rctSize);
// Initialize the RCT area
size_t rctSize = _driveInfo.RCTSize * GetBlockSize();
_rctData.reset(new uint8_t[rctSize]);
assert(_rctData != nullptr);
memset(reinterpret_cast<void *>(_rctData.get()), 0, rctSize);
}
//
// on_param_changed():
// Handles configuration parameter changes.
//
bool mscp_drive_c::on_param_changed(
parameter_c *param)
{
if (&type_name == param)
{
return SetDriveType(type_name.new_value.c_str());
}
else if (&image_filepath == param)
{
//
// Try to open the image file.
//
if (file_open(image_filepath.new_value, true))
{
image_filepath.value = image_filepath.new_value;
UpdateCapacity();
return true;
}
//
// TODO: if file is a nonstandard size?
}
else if (&use_image_size == param)
{
_useImageSize = use_image_size.new_value;
use_image_size.value = use_image_size.new_value;
UpdateCapacity();
return true;
}
return false;
}
//
// SetDriveType():
// Updates this drive's type to the specified type (i.e.
@@ -337,57 +279,46 @@ bool mscp_drive_c::on_param_changed(
// drive types, the drive's type is not changed and false
// is returned.
//
bool mscp_drive_c::SetDriveType(const char* typeName)
{
//
// Search through drive data table for name,
// and if valid, set the type appropriately.
//
int index = 0;
while (g_driveTable[index].BlockCount != 0)
{
if (!strcasecmp(typeName, g_driveTable[index].TypeName))
{
_driveInfo = g_driveTable[index];
type_name.value = _driveInfo.TypeName;
UpdateCapacity();
UpdateMetadata();
return true;
}
bool mscp_drive_c::SetDriveType(const char* typeName) {
//
// Search through drive data table for name,
// and if valid, set the type appropriately.
//
int index = 0;
while (g_driveTable[index].BlockCount != 0) {
if (!strcasecmp(typeName, g_driveTable[index].TypeName)) {
_driveInfo = g_driveTable[index];
type_name.value = _driveInfo.TypeName;
UpdateCapacity();
UpdateMetadata();
return true;
}
index++;
}
index++;
}
// Not found
return false;
// Not found
return false;
}
//
// worker():
// worker method for this drive. No work is necessary.
//
void mscp_drive_c::worker(void)
{
// Nothing to do here at the moment.
}
//
// on_power_changed():
// Handle power change notifications.
//
void mscp_drive_c::on_power_changed(void)
{
// Take the drive offline due to power change
SetOffline();
void mscp_drive_c::on_power_changed(void) {
// Take the drive offline due to power change
SetOffline();
}
//
// on_init_changed():
// Handle INIT signal.
void mscp_drive_c::on_init_changed(void)
{
// Take the drive offline due to reset
SetOffline();
void mscp_drive_c::on_init_changed(void) {
// Take the drive offline due to reset
SetOffline();
}

View File

@@ -1,129 +1,107 @@
/*
mscp_drive.hpp: Implementation of MSCP drive, used with MSCP controller.
mscp_drive.hpp: Implementation of MSCP drive, used with MSCP controller.
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
*/
#pragma once
#include <stdint.h>
#include <string.h>
#include <memory> // unique_ptr
#include "parameter.hpp"
#include "storagedrive.hpp"
//
// Implements the backing store for MSCP disk images
//
class mscp_drive_c : public storagedrive_c
{
class mscp_drive_c: public storagedrive_c {
public:
mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber);
~mscp_drive_c(void);
mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber);
~mscp_drive_c(void);
uint32_t GetBlockSize(void);
uint32_t GetBlockCount(void);
uint32_t GetRCTBlockCount(void);
uint32_t GetMediaID(void);
uint32_t GetDeviceNumber(void);
uint16_t GetClassModel(void);
uint16_t GetRCTSize(void);
uint8_t GetRBNs(void);
uint8_t GetRCTCopies(void);
bool on_param_changed(parameter_c *param) override;
void SetOnline(void);
void SetOffline(void);
bool IsOnline(void);
bool IsAvailable(void);
uint32_t GetBlockSize(void);
uint32_t GetBlockCount(void);
uint32_t GetRCTBlockCount(void);
uint32_t GetMediaID(void);
uint32_t GetDeviceNumber(void);
uint16_t GetClassModel(void);
uint16_t GetRCTSize(void);
uint8_t GetRBNs(void);
uint8_t GetRCTCopies(void);
void Write(
uint32_t blockNumber,
size_t lengthInBytes,
uint8_t* buffer);
void SetOnline(void);
void SetOffline(void);bool IsOnline(void);bool IsAvailable(void);
uint8_t* Read(
uint32_t blockNumber,
size_t lengthInBytes);
void Write(uint32_t blockNumber, size_t lengthInBytes, uint8_t* buffer);
void WriteRCTBlock(
uint32_t rctBlockNumber,
uint8_t* buffer);
uint8_t* Read(uint32_t blockNumber, size_t lengthInBytes);
uint8_t* ReadRCTBlock(
uint32_t rctBlockNumber);
void WriteRCTBlock(uint32_t rctBlockNumber, uint8_t* buffer);
uint8_t* ReadRCTBlock(uint32_t rctBlockNumber);
public:
bool on_param_changed(parameter_c *param) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
void worker(void) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
public:
parameter_bool_c use_image_size = parameter_bool_c(
this, "useimagesize", "uis", false, "Determine unit size from image file instead of drive type");
parameter_bool_c use_image_size = parameter_bool_c(this, "useimagesize", "uis", false,
"Determine unit size from image file instead of drive type");
private:
struct DriveInfo
{
char TypeName[16];
size_t BlockCount;
uint32_t MediaID;
uint8_t Model;
uint16_t RCTSize;
bool Removable;
bool ReadOnly;
};
//
// TODO: add a lot more drive types.
// Does it make sense to support drive types not native to Unibus machines (SCSI types, for example?)
// Need to add a ClassID table entry in that eventuality...
// Also TODO: RCTSize parameters taken from SIMH rq source; how valid are these?
DriveInfo g_driveTable[21]
{
struct DriveInfo {
char TypeName[16];
size_t BlockCount;
uint32_t MediaID;
uint8_t Model;
uint16_t RCTSize;bool Removable;bool ReadOnly;
};
//
// TODO: add a lot more drive types.
// Does it make sense to support drive types not native to Unibus machines (SCSI types, for example?)
// Need to add a ClassID table entry in that eventuality...
// Also TODO: RCTSize parameters taken from SIMH rq source; how valid are these?
DriveInfo g_driveTable[21] {
// Name Blocks MediaID Model RCTSize Removable ReadOnly
{ "RX50", 800, 0x25658032, 7, 0, true, false },
{ "RX33", 2400, 0x25658021, 10, 0, true, false },
{ "RD51", 21600, 0x25644033, 6, 36, false, false },
{ "RD31", 41560, 0x2564401f, 12, 3, false, false },
{ "RC25", 50902, 0x20643019, 2, 0, true, false },
{ "RC25F", 50902, 0x20643319, 3, 0, true, false },
{ "RD52", 60480, 0x25644034, 8, 4, false, false },
{ "RD32", 83236, 0x25641047, 15, 4, false, false },
{ "RD53", 138672, 0x25644035, 9, 5, false, false },
{ "RA80", 237212, 0x20643019, 1, 0, false, false },
{ "RD54", 311200, 0x25644036, 13, 7, false, false },
{ "RA60", 400176, 0x22a4103c, 4, 1008, true, false },
{ "RA70", 547041, 0x20643019, 18, 198, false, false },
{ "RA81", 891072, 0x25641051, 5, 2856, false, false },
{ "RA82", 1216665, 0x25641052, 11, 3420, false, false },
{ "RA71", 1367310, 0x25641047, 40, 1428, false, false },
{ "RA72", 1953300, 0x25641048, 37, 2040, false, false },
{ "RA90", 2376153, 0x2564105a, 19, 1794, false, false },
{ "RA92", 2940951, 0x2564105c, 29, 949, false, false },
{ "RA73", 3920490, 0x25641049, 47, 198, false, false },
{ "", 0, 0, 0, 0, false, false }
};
{ "RX50", 800, 0x25658032, 7, 0, true, false }, { "RX33", 2400, 0x25658021, 10, 0,
true, false }, { "RD51", 21600, 0x25644033, 6, 36, false, false }, { "RD31",
41560, 0x2564401f, 12, 3, false, false }, { "RC25", 50902, 0x20643019, 2, 0,
true, false }, { "RC25F", 50902, 0x20643319, 3, 0, true, false }, { "RD52",
60480, 0x25644034, 8, 4, false, false }, { "RD32", 83236, 0x25641047, 15, 4,
false, false }, { "RD53", 138672, 0x25644035, 9, 5, false, false }, {
"RA80", 237212, 0x20643019, 1, 0, false, false }, { "RD54", 311200,
0x25644036, 13, 7, false, false }, { "RA60", 400176, 0x22a4103c, 4, 1008,
true, false }, { "RA70", 547041, 0x20643019, 18, 198, false, false }, {
"RA81", 891072, 0x25641051, 5, 2856, false, false }, { "RA82", 1216665,
0x25641052, 11, 3420, false, false }, { "RA71", 1367310, 0x25641047, 40,
1428, false, false },
{ "RA72", 1953300, 0x25641048, 37, 2040, false, false }, { "RA90", 2376153,
0x2564105a, 19, 1794, false, false }, { "RA92", 2940951, 0x2564105c, 29,
949, false, false }, { "RA73", 3920490, 0x25641049, 47, 198, false, false },
{ "", 0, 0, 0, 0, false, false } };
bool SetDriveType(const char* typeName);
void UpdateCapacity(void);
void UpdateMetadata(void);
DriveInfo _driveInfo;
bool _online;
uint32_t _unitDeviceNumber;
uint16_t _unitClassModel;
bool _useImageSize;
bool SetDriveType(const char* typeName);
void UpdateCapacity(void);
void UpdateMetadata(void);
DriveInfo _driveInfo;bool _online;
uint32_t _unitDeviceNumber;
uint16_t _unitClassModel;
bool _useImageSize;
//
// RCT ("Replacement and Caching Table") data:
// The size of this area varies depending on the drive. This is
// provided only to appease software that expects the RCT to exist --
// since there will never be any bad sectors in our disk images
// there is no other purpose.
// This data is not persisted to disk as it is unnecessary.
//
unique_ptr<uint8_t> _rctData;
//
// RCT ("Replacement and Caching Table") data:
// The size of this area varies depending on the drive. This is
// provided only to appease software that expects the RCT to exist --
// since there will never be any bad sectors in our disk images
// there is no other purpose.
// This data is not persisted to disk as it is unnecessary.
//
unique_ptr<uint8_t> _rctData;
};

View File

@@ -69,11 +69,16 @@ mscp_server::mscp_server(
polling_mutex(PTHREAD_MUTEX_INITIALIZER),
_credits(INIT_CREDITS)
{
name.value = "mscp_server" ;
type_name.value = "mscp_server_c" ;
set_workers_count(0) ; // no std worker()
name.value = "mscp_server" ;
type_name.value = "mscp_server_c" ;
log_label = "MSSVR" ;
// Alias the port pointer. We do not own the port, we merely reference it.
_port = port;
enabled.set(true) ;
enabled.readonly = true ; // always active
StartPollingThread();
}
@@ -83,6 +88,18 @@ mscp_server::~mscp_server()
AbortPollingThread();
}
bool mscp_server::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic
if (param == &enabled) {
// accpet, but do not react on enable/disable, always active
return true ;
}
return device_c::on_param_changed(param) ; // more actions (for enable)
}
//
// StartPollingThread():
// Initializes the MSCP polling thread and starts it running.

View File

@@ -143,6 +143,7 @@ class mscp_server : public device_c
public:
mscp_server(uda_c *port);
~mscp_server();
bool on_param_changed(parameter_c *param) override ;
public:
void Reset(void);
@@ -152,8 +153,6 @@ public:
public:
void on_power_changed(void) override {}
void on_init_changed(void) override {}
void worker(void) override {}
bool on_param_changed(parameter_c *param) override { UNUSED(param); return true; }
private:
uint32_t Abort(void);

View File

@@ -1,43 +1,43 @@
/* panels.cpp: a device to access lamps & buttons connected over I2C bus
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
a device to access lamps & buttons connected over I2C bus
Up to 8 MC23017 GPIO extenders each with 16 I/Os can be connected.
a device to access lamps & buttons connected over I2C bus
Up to 8 MC23017 GPIO extenders each with 16 I/Os can be connected.
Other devices register some of their bit-Parameters with IOs.
Other devices register some of their bit-Parameters with IOs.
1 i2c driver - n panels
1 paneldriver - controls for many devices of same type (4 buttons for each of 4 RL02s)
1 control - identifed by unique combination of device name and control name
("rl1", "load_button")
device name ideally same as device-> name
control name ideally same as deviceparameter->name
1 i2c driver - n panels
1 paneldriver - controls for many devices of same type (4 buttons for each of 4 RL02s)
1 control - identifed by unique combination of device name and control name
("rl1", "load_button")
device name ideally same as device-> name
control name ideally same as deviceparameter->name
! Static list of panel controls is consntat,
but device parameters connected to controls is dynamic
(run time device creation/deletion)
! Static list of panel controls is consntat,
but device parameters connected to controls is dynamic
(run time device creation/deletion)
*/
#include <stdint.h>
@@ -159,6 +159,11 @@ paneldriver_c::~paneldriver_c() {
unregister_controls();
}
bool paneldriver_c::on_param_changed(parameter_c *param) {
// no own parameter logic
return device_c::on_param_changed(param);
}
/* low level access to I2C bus slaves */
// https://elinux.org/Interfacing_with_I2C_Devices#Opening_the_Bus
// result: true = success
@@ -208,7 +213,7 @@ bool paneldriver_c::i2c_write_byte(uint8_t slave_addr, uint8_t reg_addr, uint8_t
// reprogram the I2C chips and restart the worker
void paneldriver_c::reset(void) {
timeout_c timeout;
worker_stop();
enabled.set(false); // worker_stop();
// pulse "panel_reset_l"
// MC32017: at least 1 us
@@ -245,12 +250,7 @@ void paneldriver_c::reset(void) {
i2c_write_byte(slave_addr, MC23017_GPPUB, 0xff);
}
worker_start();
}
bool paneldriver_c::on_param_changed(parameter_c *param) {
UNUSED(param);
return true ;
enabled.set(true); // worker_start();
}
void paneldriver_c::on_power_changed(void) {
@@ -438,10 +438,11 @@ void paneldriver_c::i2c_sync_all_params() {
Query all used I2C chip register,
Update controls and parameters
*/
void paneldriver_c::worker(void) {
void paneldriver_c::worker(unsigned instance) {
UNUSED(instance); // only one
timeout_c timeout;
while (!worker_terminate) {
while (!workers_terminate) {
// poll in endless round
i2c_sync_all_params();
timeout.wait_ms(10);
@@ -468,7 +469,7 @@ void paneldriver_c::test_moving_ones(void) {
clear_all_outputs();
timeout.wait_ms(delay_ms);
// all "OFF" on exit
worker_stop() ;
enabled.set(false); // worker_stop();
INFO("Worker stopped.");
}
@@ -490,7 +491,7 @@ void paneldriver_c::test_manual_loopback(void) {
(*it)->associate->value = (*it)->value;
timeout.wait_ms(10);
}
worker_stop() ;
enabled.set(false); // worker_stop();
INFO("Worker stopped.");
}

View File

@@ -1,28 +1,28 @@
/* panels.hpp: a device to access lamps & buttons connected over I2C bus
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#include "utils.hpp"
#include "device.hpp"
@@ -72,7 +72,7 @@ public:
unsigned value; // input or output
// output lamps can be linekd to their including buttons
panelcontrol_c *associate ;
panelcontrol_c *associate;
panelcontrol_c(string device_name, string control_name,
bool is_input, uint8_t chip_addr, uint8_t reg_addr, uint8_t reg_bitmask);
@@ -94,6 +94,8 @@ public:
paneldriver_c();
~paneldriver_c();
bool on_param_changed(parameter_c *param) override; // must implement
vector<panelcontrol_c *> controls;
void unregister_controls(void); // clear static list of all connected controls
@@ -110,18 +112,15 @@ public:
void i2c_sync_all_params();
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override; // must implement
void on_init_changed(void) override; // must implement
void clear_all_outputs(void) ;
void clear_all_outputs(void);
// link_control_to_parameter(new panelcontrol_c()) ;
void link_control_to_parameter(parameter_c *deviceparameter,
panelcontrol_c *panelcontrol);
void link_control_to_parameter(parameter_c *deviceparameter, panelcontrol_c *panelcontrol);
void unlink_controls_from_device(device_c *device);
void refresh_params(device_c *device);

View File

@@ -1,10 +1,10 @@
/*
rk05.cpp: implementation of RK05 disk drive, attached to RK11D controller
rk05.cpp: implementation of RK05 disk drive, attached to RK11D controller
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
*/
#include <assert.h>
@@ -16,304 +16,250 @@ using namespace std;
#include "rk05.hpp"
rk05_c::rk05_c(storagecontroller_c *controller) :
storagedrive_c(controller),
_current_cylinder(0),
_seek_count(0),
_sectorCount(0),
_wps(false),
_rwsrdy(true),
_dry(false),
_sok(false),
_sin(false),
_dru(false),
_rk05(true),
_dpl(false),
_scp(false)
{
type_name.value = "RK05" ;
log_label = "RK05";
_geometry.Cylinders = 203; // Standard RK05
_geometry.Heads = 2;
_geometry.Sectors = 12;
_geometry.Sector_Size_Bytes = 512;
storagedrive_c(controller), _current_cylinder(0), _seek_count(0), _sectorCount(0), _wps(
false), _rwsrdy(true), _dry(false), _sok(false), _sin(false), _dru(false), _rk05(
true), _dpl(false), _scp(false) {
name.value = "RK05";
type_name.value = "RK05";
log_label = "RK05";
_geometry.Cylinders = 203; // Standard RK05
_geometry.Heads = 2;
_geometry.Sectors = 12;
_geometry.Sector_Size_Bytes = 512;
}
//
// Status registers
//
uint32_t rk05_c::get_sector_counter(void)
{
return _sectorCount;
uint32_t rk05_c::get_sector_counter(void) {
return _sectorCount;
}
bool rk05_c::get_write_protect(void)
{
return _wps;
bool rk05_c::get_write_protect(void) {
return _wps;
}
bool rk05_c::get_rws_ready(void)
{
return _rwsrdy;
bool rk05_c::get_rws_ready(void) {
return _rwsrdy;
}
bool rk05_c::get_drive_ready(void)
{
return _dry;
bool rk05_c::get_drive_ready(void) {
return _dry;
}
bool rk05_c::get_sector_counter_ok(void)
{
return _sok;
bool rk05_c::get_sector_counter_ok(void) {
return _sok;
}
bool rk05_c::get_seek_incomplete(void)
{
return _sin;
bool rk05_c::get_seek_incomplete(void) {
return _sin;
}
bool rk05_c::get_drive_unsafe(void)
{
return _dru;
bool rk05_c::get_drive_unsafe(void) {
return _dru;
}
bool rk05_c::get_rk05_disk_online(void)
{
return _rk05;
bool rk05_c::get_rk05_disk_online(void) {
return _rk05;
}
bool rk05_c::get_drive_power_low(void)
{
return _dpl;
bool rk05_c::get_drive_power_low(void) {
return _dpl;
}
bool rk05_c::get_search_complete(void)
{
bool scp = _scp;
_scp = false;
return scp;
bool rk05_c::get_search_complete(void) {
bool scp = _scp;
_scp = false;
return scp;
}
bool rk05_c::on_param_changed(
parameter_c *param)
{
if (&image_filepath == param)
{
if (file_open(image_filepath.new_value, true))
{
_dry = true;
controller->on_drive_status_changed(this);
image_filepath.value = image_filepath.new_value;
return true;
}
}
return false;
bool rk05_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
if (!enabled.new_value) {
// disable switches power OFF.
drive_reset();
}
} else if (&image_filepath == param) {
if (file_open(image_filepath.new_value, true)) {
_dry = true;
controller->on_drive_status_changed(this);
image_filepath.value = image_filepath.new_value;
return true;
}
}
return storagedrive_c::on_param_changed(param); // more actions (for enable)
}
//
// Reset / Power handlers
//
void rk05_c::on_power_changed(void)
{
// called at high priority.
if (power_down)
{
// power-on defaults
drive_reset();
}
void rk05_c::on_power_changed(void) {
// called at high priority.
if (power_down) {
// power-on defaults
drive_reset();
}
}
void rk05_c::on_init_changed(void)
{
// called at high priority.
void rk05_c::on_init_changed(void) {
// called at high priority.
if (init_asserted)
{
drive_reset();
}
if (init_asserted) {
drive_reset();
}
}
//
// Disk actions (read/write/seek/reset)
//
void rk05_c::read_sector(
uint32_t cylinder,
uint32_t surface,
uint32_t sector,
uint16_t* out_buffer)
{
assert(cylinder < _geometry.Cylinders);
assert(surface < _geometry.Heads);
assert(sector < _geometry.Sectors);
void rk05_c::read_sector(uint32_t cylinder, uint32_t surface, uint32_t sector,
uint16_t* out_buffer) {
assert(cylinder < _geometry.Cylinders);
assert(surface < _geometry.Heads);
assert(sector < _geometry.Sectors);
_current_cylinder = cylinder;
_current_cylinder = cylinder;
// SCP is cleared at the start of any function.
_scp = false;
// SCP is cleared at the start of any function.
_scp = false;
//
// reset Read/Write/Seek Ready flag while we do this operation
//
_rwsrdy = false;
controller->on_drive_status_changed(this);
//
// reset Read/Write/Seek Ready flag while we do this operation
//
_rwsrdy = false;
controller->on_drive_status_changed(this);
timeout_c delay;
// Delay for seek / read.
// TODO: maybe base this on real drive specs.
delay.wait_ms(10);
timeout_c delay;
// Read the sector into the buffer passed to us.
file_read(
reinterpret_cast<uint8_t*>(out_buffer),
get_disk_byte_offset(cylinder, surface, sector),
_geometry.Sector_Size_Bytes);
// Delay for seek / read.
// TODO: maybe base this on real drive specs.
delay.wait_ms(10);
// Set RWS ready now that we're done.
_rwsrdy = true;
// Read the sector into the buffer passed to us.
file_read(reinterpret_cast<uint8_t*>(out_buffer),
get_disk_byte_offset(cylinder, surface, sector), _geometry.Sector_Size_Bytes);
controller->on_drive_status_changed(this);
// Set RWS ready now that we're done.
_rwsrdy = true;
controller->on_drive_status_changed(this);
}
void rk05_c::write_sector(
uint32_t cylinder,
uint32_t surface,
uint32_t sector,
uint16_t* in_buffer)
{
assert(cylinder < _geometry.Cylinders);
assert(surface < _geometry.Heads);
assert(sector < _geometry.Sectors);
void rk05_c::write_sector(uint32_t cylinder, uint32_t surface, uint32_t sector,
uint16_t* in_buffer) {
assert(cylinder < _geometry.Cylinders);
assert(surface < _geometry.Heads);
assert(sector < _geometry.Sectors);
_current_cylinder = cylinder;
_current_cylinder = cylinder;
// SCP is cleared at the start of any function.
_scp = false;
// SCP is cleared at the start of any function.
_scp = false;
//
// reset Read/Write/Seek Ready flag while we do this operation
//
_rwsrdy = false;
controller->on_drive_status_changed(this);
//
// reset Read/Write/Seek Ready flag while we do this operation
//
_rwsrdy = false;
controller->on_drive_status_changed(this);
timeout_c delay;
timeout_c delay;
// Delay for seek / read.
// TODO: maybe base this on real drive specs.
delay.wait_ms(10);
// Delay for seek / read.
// TODO: maybe base this on real drive specs.
delay.wait_ms(10);
// Read the sector into the buffer passed to us.
file_write(
reinterpret_cast<uint8_t*>(in_buffer),
get_disk_byte_offset(cylinder, surface, sector),
_geometry.Sector_Size_Bytes);
// Read the sector into the buffer passed to us.
file_write(reinterpret_cast<uint8_t*>(in_buffer),
get_disk_byte_offset(cylinder, surface, sector), _geometry.Sector_Size_Bytes);
// Set RWS ready now that we're done.
_rwsrdy = true;
// Set RWS ready now that we're done.
_rwsrdy = true;
controller->on_drive_status_changed(this);
controller->on_drive_status_changed(this);
}
void rk05_c::seek(
uint32_t cylinder)
{
assert(cylinder < _geometry.Cylinders);
void rk05_c::seek(uint32_t cylinder) {
assert(cylinder < _geometry.Cylinders);
_seek_count = abs((int32_t)_current_cylinder - (int32_t)cylinder) + 1;
_current_cylinder = cylinder;
_seek_count = abs((int32_t) _current_cylinder - (int32_t) cylinder) + 1;
_current_cylinder = cylinder;
if (_seek_count > 0)
{
// We'll be busy for awhile:
_rwsrdy = false;
_scp = false;
}
else
{
_rwsrdy = true;
_scp = true;
}
controller->on_drive_status_changed(this);
if (_seek_count > 0) {
// We'll be busy for awhile:
_rwsrdy = false;
_scp = false;
} else {
_rwsrdy = true;
_scp = true;
}
controller->on_drive_status_changed(this);
}
void rk05_c::set_write_protect(bool protect)
{
UNUSED(protect);
void rk05_c::set_write_protect(bool protect) {
UNUSED(protect);
// Not implemented at the moment.
_scp = false;
// Not implemented at the moment.
_scp = false;
}
void rk05_c::drive_reset(void)
{
//
// "The controller directs the selected disk drive to move its
// head mechanism to cylinder address 000 and reset all active
// error status lines."
//
// This is basically the same as a seek to cylinder 0 plus
// a reset of error status.
//
_sin = false;
_dru = false;
_dpl = false;
controller->on_drive_status_changed(this);
void rk05_c::drive_reset(void) {
//
// "The controller directs the selected disk drive to move its
// head mechanism to cylinder address 000 and reset all active
// error status lines."
//
// This is basically the same as a seek to cylinder 0 plus
// a reset of error status.
//
_sin = false;
_dru = false;
_dpl = false;
controller->on_drive_status_changed(this);
seek(0);
// SCP change will be posted when the seek instigated above is completed.
seek(0);
// SCP change will be posted when the seek instigated above is completed.
}
void rk05_c::worker(void)
{
timeout_c timeout;
void rk05_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
while(true)
{
if (_seek_count > 0)
{
// A seek is active. Wait at least 10ms and decrement
// The seek count by a certain amount. This is completely fudged.
timeout.wait_ms(3);
_seek_count -= 25;
// since simultaneous interrupts
// confuse me right now
while (true) {
if (_seek_count > 0) {
// A seek is active. Wait at least 10ms and decrement
// The seek count by a certain amount. This is completely fudged.
timeout.wait_ms(3);
_seek_count -= 25;
// since simultaneous interrupts
// confuse me right now
if (_seek_count < 0)
{
// Out of seeks to do, let the controller know we're done.
_scp = true;
controller->on_drive_status_changed(this);
if (_seek_count < 0) {
// Out of seeks to do, let the controller know we're done.
_scp = true;
controller->on_drive_status_changed(this);
// Set RWSRDY only after posting status change / interrupt...
_rwsrdy = true;
}
}
else
{
// Move SectorCounter to next sector
// every 1/300th of a second (or so).
// (1600 revs/min = 25 revs / sec = 300 sectors / sec)
timeout.wait_ms(3);
if (file_is_open())
{
_sectorCount = (_sectorCount + 1) % 12;
_sok = true;
controller->on_drive_status_changed(this);
}
}
}
// Set RWSRDY only after posting status change / interrupt...
_rwsrdy = true;
}
} else {
// Move SectorCounter to next sector
// every 1/300th of a second (or so).
// (1600 revs/min = 25 revs / sec = 300 sectors / sec)
timeout.wait_ms(3);
if (file_is_open()) {
_sectorCount = (_sectorCount + 1) % 12;
_sok = true;
controller->on_drive_status_changed(this);
}
}
}
}
uint64_t rk05_c::get_disk_byte_offset(
uint32_t cylinder,
uint32_t surface,
uint32_t sector)
{
return _geometry.Sector_Size_Bytes *
((cylinder * _geometry.Heads * _geometry.Sectors) +
(surface * _geometry.Sectors) +
sector);
uint64_t rk05_c::get_disk_byte_offset(uint32_t cylinder, uint32_t surface, uint32_t sector) {
return _geometry.Sector_Size_Bytes
* ((cylinder * _geometry.Heads * _geometry.Sectors) + (surface * _geometry.Sectors)
+ sector);
}

5
10.02_devices/2_src/rk05.hpp Executable file → Normal file
View File

@@ -91,12 +91,13 @@ public:
rk05_c(storagecontroller_c *controller);
bool on_param_changed(parameter_c* param) override;
bool on_param_changed(parameter_c* param) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
};
#endif

View File

@@ -25,10 +25,13 @@ rk11_c::rk11_c() :
name.value = "rk";
type_name.value = "RK11";
log_label = "rk";
default_base_addr = 0777400; // overwritten in install()?
default_intr_vector = 0220; // TODO: make configurable
default_intr_level = 5; // TODO: make configurable
// base addr, intr-vector, intr level
set_default_bus_params(0777400, 10, 0220, 5) ;
dma_request.set_priority_slot(default_priority_slot) ;
intr_request.set_priority_slot(default_priority_slot) ;
intr_request.set_level(default_intr_level) ;
intr_request.set_vector(default_intr_vector) ;
// The RK11 controller has seven registers,
// We allocate 8 because one address in the address space is unused.
@@ -117,6 +120,14 @@ rk11_c::~rk11_c()
}
}
// return false, if illegal parameter value.
// verify "new_value", must output error messages
bool rk11_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
}
void rk11_c::dma_transfer(DMARequest &request)
{
timeout_c timeout;
@@ -133,23 +144,24 @@ void rk11_c::dma_transfer(DMARequest &request)
{
// Write FROM buffer TO unibus memory, IBA on:
// We only need to write the last word in the buffer to memory.
request.timeout = !unibusadapter->request_client_DMA(
unibusadapter->DMA(dma_request, true,
UNIBUS_CONTROL_DATO,
request.address,
request.buffer + request.count - 1,
1, NULL);
1);
request.timeout = !dma_request.success ;
}
else
{
// Read FROM unibus memory TO buffer, IBA on:
// We read a single word from the unibus and fill the
// entire buffer with this value.
request.timeout = !unibusadapter->request_client_DMA(
unibusadapter->DMA(dma_request, true,
UNIBUS_CONTROL_DATI,
request.address,
request.buffer,
1,
NULL);
1);
request.timeout = !dma_request.success ;
}
}
else
@@ -158,22 +170,22 @@ void rk11_c::dma_transfer(DMARequest &request)
if (request.write)
{
// Write FROM buffer TO unibus memory
request.timeout = !unibusadapter->request_client_DMA(
unibusadapter->DMA(dma_request, true,
UNIBUS_CONTROL_DATO,
request.address,
request.buffer,
request.count,
NULL);
request.count);
request.timeout = !dma_request.success ;
}
else
{
// Read FROM unibus memory TO buffer
request.timeout = !unibusadapter->request_client_DMA(
unibusadapter->DMA(dma_request, true,
UNIBUS_CONTROL_DATI,
request.address,
request.buffer,
request.count,
NULL);
request.count);
request.timeout = !dma_request.success ;
}
}
@@ -190,8 +202,9 @@ void rk11_c::dma_transfer(DMARequest &request)
// Background worker.
// Handle device operations.
void rk11_c::worker(void)
void rk11_c::worker(unsigned instance)
{
UNUSED(instance) ; // only one
worker_init_realtime_priority(rt_device);
@@ -200,7 +213,7 @@ void rk11_c::worker(void)
bool do_interrupt = true;
timeout_c timeout;
while (!worker_terminate)
while (!workers_terminate)
{
switch (_worker_state)
{
@@ -952,7 +965,7 @@ void rk11_c::invoke_interrupt(void)
//
if (_ide)
{
interrupt();
unibusadapter->INTR(intr_request, NULL, 0); // todo: link to interupt register
}
}

View File

@@ -14,6 +14,7 @@
using namespace std;
#include "utils.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
#include "storagecontroller.hpp"
#include "rk05.hpp"
@@ -149,19 +150,27 @@ private:
Worker_Finish = 2,
} _worker_state;
// Unibusadapter: RK11 has one INTR and DMA
// should be merged with RK11::DMARequest
dma_request_c dma_request = dma_request_c(this) ; // operated by unibusadapter
intr_request_c intr_request = intr_request_c(this) ;
public:
rk11_c();
virtual ~rk11_c();
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(
unibusdevice_register_t *device_reg,
uint8_t unibus_control) override;
bool on_param_changed(parameter_c *param) override;
void on_power_changed(void) override;
void on_init_changed(void) override;

View File

@@ -1,28 +1,28 @@
/* rl02.cpp: implementation of RL01/RL02 disk drive, attached to RL11 controller
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#include <assert.h>
@@ -44,22 +44,28 @@ RL0102_c::RL0102_c(storagecontroller_c *controller) :
}
// return false, if illegal parameter value.
// verify "new_value", must output error messages
bool RL0102_c::on_param_changed(parameter_c *param) {
if (param == &type_name) {
if (param == &enabled) {
if (!enabled.new_value) {
// disable switches power OFF.
// must be power on by caller or user after enable
power_switch.value = false;
change_state(RL0102_STATE_power_off);
}
} else if (param == &type_name) {
if (!strcasecmp(type_name.new_value.c_str(), "RL01"))
set_type(1) ;
set_type(1);
else if (!strcasecmp(type_name.new_value.c_str(), "RL02"))
set_type(2) ;
set_type(2);
else {
// throw bad_parameter_check("drive type must be RL01 or RL02") ;
ERROR("drive type must be RL01 or RL02") ;
return false ;
ERROR("drive type must be RL01 or RL02");
return false;
}
}
return true ;
return storagedrive_c::on_param_changed(param); // more actions (for enable)
}
void RL0102_c::set_type(uint8_t drivetype) {
@@ -168,15 +174,15 @@ void RL0102_c::change_state(unsigned new_state) {
state.value = new_state;
update_status_word(); // contains state
if (old_state != new_state)
DEBUG("Change drive %s state from %d to %d. Status word %06o -> %06o.", name.value.c_str(),
old_state, state.value, old_status_word, status_word);
DEBUG("Change drive %s state from %d to %d. Status word %06o -> %06o.",
name.value.c_str(), old_state, state.value, old_status_word, status_word);
}
/*** state functions, called repeatedly ***/
void RL0102_c::state_power_off() {
// drive_ready_line = false; // verified
// drive_error_line = true; // real RL02: RL11 show a DRIVE ERROR after power on / DC_LO
type_name.readonly = false ; // may be changed between RL01/RL02
type_name.readonly = false; // may be changed between RL01/RL02
volume_check = true; // starts with volume check?
cover_open.readonly = true;
update_status_word(/*drive_ready_line*/false, /*drive_error_line*/true);
@@ -195,7 +201,7 @@ void RL0102_c::state_power_off() {
// drive stop, door unlocked, cartridge can be loaded
void RL0102_c::state_load_cartridge() {
// drive_ready_line = false; // verified
type_name.readonly = true ; // must be powered of to changed between RL01/RL02
type_name.readonly = true; // must be powered of to changed between RL01/RL02
update_status_word(/*drive_ready_line*/false, drive_error_line);
load_lamp.value = 1;
ready_lamp.value = 0;
@@ -256,7 +262,7 @@ void RL0102_c::state_spin_up() {
load_lamp.value = 0;
ready_lamp.value = 0;
writeprotect_lamp.value = writeprotect_button.value || file_readonly ;
writeprotect_lamp.value = writeprotect_button.value || file_readonly;
image_filepath.readonly = true; // "door locked", disk can not be changed
state_timeout.wait_ms(calcperiod_ms);
@@ -377,7 +383,7 @@ void RL0102_c::state_lock_on() {
load_lamp.value = 0;
ready_lamp.value = 1;
writeprotect_lamp.value = writeprotect_button.value|| file_readonly;
writeprotect_lamp.value = writeprotect_button.value || file_readonly;
// fast polling, if ZRLI tests time of 0 cly seek with head switch
state_timeout.wait_ms(1);
@@ -501,7 +507,7 @@ bool RL0102_c::header_on_track(uint16_t header) {
} while(0)
#ifdef OLD
#define NEXT_SECTOR_SEGMENT_ADVANCE do { \
#define NEXT_SECTOR_SEGMENT_ADVANCE do { \
next_sector_segment_under_heads = (next_sector_segment_under_heads + 1) % 80 ; \
if (next_sector_segment_under_heads & 1) \
/* time to pass one sector 600 data, 25 header? */ \
@@ -526,7 +532,8 @@ bool RL0102_c::cmd_read_next_sector_header(uint16_t *buffer, unsigned buffer_siz
if (next_sector_segment_under_heads & 1) {
// odd: next is data, let it pass the head
NEXT_SECTOR_SEGMENT_ADVANCE;
NEXT_SECTOR_SEGMENT_ADVANCE
;
// nanosleep() for rotational delay?
}
@@ -542,7 +549,8 @@ bool RL0102_c::cmd_read_next_sector_header(uint16_t *buffer, unsigned buffer_siz
buffer[2] = calc_crc(2, &buffer[0]); // header CRC
// circular advance to next header: 40x headers, 40x data
NEXT_SECTOR_SEGMENT_ADVANCE;
NEXT_SECTOR_SEGMENT_ADVANCE
;
// nanosleep() for rotational delay?
return true;
}
@@ -558,7 +566,8 @@ bool RL0102_c::cmd_read_next_sector_data(uint16_t *buffer, unsigned buffer_size_
if (!(next_sector_segment_under_heads & 1)) {
// even: next segment is header, let it pass the head
NEXT_SECTOR_SEGMENT_ADVANCE;
NEXT_SECTOR_SEGMENT_ADVANCE
;
// nanosleep() for rotational delay?
}
unsigned sectorno = next_sector_segment_under_heads >> 1; // LSB is header/data phase
@@ -570,13 +579,12 @@ bool RL0102_c::cmd_read_next_sector_data(uint16_t *buffer, unsigned buffer_size_
// LSB saved before MSB -> word/byte conversion on ARM (little endian) is easy
file_read((uint8_t *) buffer, offset, sector_size_bytes);
DEBUG("File Read 0x%x words from c/h/s=%d/%d/%d, file pos=0x%llx, words = %06o, %06o, ...",
sector_size_bytes/2,
cylinder,head, sectorno,
offset, (unsigned)(buffer[0]), (unsigned)(buffer[1])) ;
sector_size_bytes / 2, cylinder, head, sectorno, offset, (unsigned )(buffer[0]),
(unsigned )(buffer[1]));
// circular advance to next header: 40x headers, 40x data
NEXT_SECTOR_SEGMENT_ADVANCE;
NEXT_SECTOR_SEGMENT_ADVANCE
;
// nanosleep() for rotational delay?
return true;
}
@@ -600,7 +608,8 @@ bool RL0102_c::cmd_write_next_sector_data(uint16_t *buffer, unsigned buffer_size
if (!(next_sector_segment_under_heads & 1)) {
// even: next segment is header, let it pass the head
NEXT_SECTOR_SEGMENT_ADVANCE;
NEXT_SECTOR_SEGMENT_ADVANCE
;
// nanosleep() for rotational delay?
}
unsigned sectorno = next_sector_segment_under_heads >> 1; // LSB is header/data phase
@@ -612,33 +621,38 @@ bool RL0102_c::cmd_write_next_sector_data(uint16_t *buffer, unsigned buffer_size
// LSB saved before MSB -> word/byte conversion on ARM (little endian) is easy
file_write((uint8_t *) buffer, offset, sector_size_bytes);
DEBUG("File Write 0x%x words from c/h/s=%d/%d/%d, file pos=0x%llx, words = %06o, %06o, ...",
sector_size_bytes/2,
cylinder,head, sectorno,
offset, (unsigned)(buffer[0]), (unsigned)(buffer[1])) ;
sector_size_bytes / 2, cylinder, head, sectorno, offset, (unsigned )(buffer[0]),
(unsigned )(buffer[1]));
// circular advance to next header: 40x headers, 40x data
NEXT_SECTOR_SEGMENT_ADVANCE;
NEXT_SECTOR_SEGMENT_ADVANCE
;
// nanosleep() for rotational delay?
return true;
}
// thread
void RL0102_c::worker(void) {
void RL0102_c::worker(unsigned instance) {
UNUSED(instance); // only one
timeout_c timeout;
// set prio to RT, but less than RL11 controller
worker_init_realtime_priority(rt_device);
while (!worker_terminate) {
while (!workers_terminate) {
// worker_mutex.lock() ; // collision with cmd_seek() and on_xxx_changed()
// states have set error flags not in RL11 CSR: just update
update_status_word(drive_ready_line, drive_error_line);
// global stuff for all states
if (enabled.value && (!controller || !controller->enabled.value))
// RL drive powered, but no controller: no clock -> FAULT
fault_lamp.value = true;
if (power_switch.value == false)
change_state(RL0102_STATE_power_off);
switch (state.value) {
case RL0102_STATE_power_off:
state_power_off();

View File

@@ -1,28 +1,28 @@
/* rl02.cpp: implementation of RL01/RL02 disk drive, attached to RL11 controller
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _RL0102_HPP_
#define _RL0102_HPP_
@@ -120,8 +120,8 @@ public:
volatile uint16_t status_word; // visible to controller
parameter_unsigned_c rotation_umin = parameter_unsigned_c(this, "rotation",
"rot",/*readonly*/true, "rpm", "%d", "Current speed of disk", 32, 10);
parameter_unsigned_c rotation_umin = parameter_unsigned_c(this, "rotation", "rot",/*readonly*/
true, "rpm", "%d", "Current speed of disk", 32, 10);
// RL0102_STATE_*. no enum, is param
parameter_unsigned_c state = parameter_unsigned_c(this, "state", "st", /*readonly*/
true, "", "%d", "Internal state", 32, 10);
@@ -137,17 +137,16 @@ public:
true, "State of READY lamp");
parameter_bool_c fault_lamp = parameter_bool_c(this, "faultlamp", "fl", /*readonly*/
true, "State of FAULT lamp");
parameter_bool_c writeprotect_lamp = parameter_bool_c(this, "writeprotectlamp",
"wpl", /*readonly*/true, "State of WRITE PROTECT lamp");
parameter_bool_c writeprotect_button = parameter_bool_c(this,
"writeprotectbutton", "wpb", /*readonly*/false, "Writeprotect button pressed");
parameter_bool_c writeprotect_lamp = parameter_bool_c(this, "writeprotectlamp", "wpl", /*readonly*/
true, "State of WRITE PROTECT lamp");
parameter_bool_c writeprotect_button = parameter_bool_c(this, "writeprotectbutton", "wpb", /*readonly*/
false, "Writeprotect button pressed");
// cover normally always "closed", need to get opened for ZRLI
parameter_bool_c cover_open = parameter_bool_c(this, "coveropen", "co", /*readonly*/
false, "1, if RL cover is open");
// not readonly only in "load" state
RL0102_c(storagecontroller_c *controller);
bool on_param_changed(parameter_c *param) override;
@@ -188,7 +187,7 @@ public:
void clear_error_register(void);
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
};
#endif

View File

@@ -1,86 +1,86 @@
/* rl11.cpp: Implementation of the RL11 controller
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
12-nov-2018 JH entered beta phase
- implements a 4 UNIBUS register interface, which are shared with PRU.
- gets notified of UNIBUS register access on_after_register_access()
- starts 4 RL01/02 drives
on_after_register_access() is a high priority RT thread.
It may ONLY update the settings of UNIBUS interface registers by swapping in
several internal registers (status for each drive, MP multipuprpose for different
Commands) to UNIBUS registers.
- execution of commands, access to drives etc is made in worker()
worker() is waked by a signal from on_after_register_access()
- implements a 4 UNIBUS register interface, which are shared with PRU.
- gets notified of UNIBUS register access on_after_register_access()
- starts 4 RL01/02 drives
on_after_register_access() is a high priority RT thread.
It may ONLY update the settings of UNIBUS interface registers by swapping in
several internal registers (status for each drive, MP multipuprpose for different
Commands) to UNIBUS registers.
- execution of commands, access to drives etc is made in worker()
worker() is waked by a signal from on_after_register_access()
Todo:
- operation, when drive power OFF? error DSE drive select?
1) RL0 powered off: CS =200, nach NOP
Get status; DA=013, write 00004 Read: CS 102204 (ERR,OPI, DRVready=0
MP = 006050 (3x identisch)
Seek: Write 0006, read: 102206 (Spnerror, coveropen,brush home)
MP = 20210
2) RL0 powered on, LOAD
NOOP: CS write 0, read 200
Get Status: DA=013,write 0004, read 204. MP = 20217 (spinn down), dann 20210 (LOAD)
Seek: Write CS=0006 , read 102206. MP = unchanged
3) RL02 on track
NOOP: CS write 0, read 140201 (Driverer, do Getstatus
Get Status: DA=013, write 0004, read 205. MP = 020235
Seek: DA=0377 (255)100, read = 207
Todo:
- operation, when drive power OFF? error DSE drive select?
1) RL0 powered off: CS =200, nach NOP
Get status; DA=013, write 00004 Read: CS 102204 (ERR,OPI, DRVready=0
MP = 006050 (3x identisch)
Seek: Write 0006, read: 102206 (Spnerror, coveropen,brush home)
MP = 20210
2) RL0 powered on, LOAD
NOOP: CS write 0, read 200
Get Status: DA=013,write 0004, read 204. MP = 20217 (spinn down), dann 20210 (LOAD)
Seek: Write CS=0006 , read 102206. MP = unchanged
3) RL02 on track
NOOP: CS write 0, read 140201 (Driverer, do Getstatus
Get Status: DA=013, write 0004, read 205. MP = 020235
Seek: DA=0377 (255)100, read = 207
- Which errors raise "OPI" (operation incomplete)
NXM?
- Mismatch DMA wordcount and sector buffer
word len != sector border ? => no problem?
end of track before worldcount == 0 ?
"DA register is not incrmeneted in multisector transfer"
=> OPI?
- Which errors raise "OPI" (operation incomplete)
NXM?
- Mismatch DMA wordcount and sector buffer
word len != sector border ? => no problem?
end of track before worldcount == 0 ?
"DA register is not incrmeneted in multisector transfer"
=> OPI?
- "Read header: ": select which sector to read?
Simulate disk rotation???
How to generate CRC? -> simh!
- "Read header: ": select which sector to read?
Simulate disk rotation???
How to generate CRC? -> simh!
- "read data without header"
-> wait for sector pulse? disk rotation?
- "read data without header"
-> wait for sector pulse? disk rotation?
Communication between on_after_register_access and worker():
- use pthread condition variable pthrad_cond_*
- normally a mutex show protect worker() against variable change
by interrupting on_after_register_access()
- the signal "controller_ready" is that mutex already:
set by cmd-start in on_after_register_access(),
released by worker() on completion
- still a mutex needed, only for the thread condition variable as shown in
- mutex in on_after_register_access() and worker()
- all refgsier access are atomic 32bit anyhow
http://openbook.rheinwerk-verlag.de/linux_unix_programmierung/Kap10-006.htm#RxxKap10006040003201F02818E
https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032r/index.html#sync-59145
Communication between on_after_register_access and worker():
- use pthread condition variable pthrad_cond_*
- normally a mutex show protect worker() against variable change
by interrupting on_after_register_access()
- the signal "controller_ready" is that mutex already:
set by cmd-start in on_after_register_access(),
released by worker() on completion
- still a mutex needed, only for the thread condition variable as shown in
- mutex in on_after_register_access() and worker()
- all refgsier access are atomic 32bit anyhow
http://openbook.rheinwerk-verlag.de/linux_unix_programmierung/Kap10-006.htm#RxxKap10006040003201F02818E
https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032r/index.html#sync-59145
*/
@@ -136,9 +136,12 @@ RL11_c::RL11_c(void) :
type_name.value = "RL11";
log_label = "rl";
default_base_addr = 0774400;
default_intr_vector = 0160;
default_intr_level = 5;
// base addr, intr-vector, intr level
set_default_bus_params(0774400, 15, 0160, 5);
dma_request.set_priority_slot(default_priority_slot);
intr_request.set_priority_slot(default_priority_slot);
intr_request.set_level(default_intr_level);
intr_request.set_vector(default_intr_vector);
// add 4 RL disk drives
drivecount = 4;
@@ -194,6 +197,19 @@ RL11_c::~RL11_c() {
delete storagedrives[i];
}
bool RL11_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
if (enabled.new_value) {
// enabled
connect_to_panel();
} else {
// disabled
disconnect_from_panel();
}
}
return storagecontroller_c::on_param_changed(param); // more actions (for enable)
}
/* connect parameters of drives to i2c paneldriver
* Changes to parameter values after user panel operation
* or refresh_params_from_panel()
@@ -263,9 +279,10 @@ void RL11_c::reset(void) {
interrupt_enable = 0;
unibus_address_msb = 0;
clear_errors();
intr_request.edge_detect_reset();
change_state(RL11_STATE_CONTROLLER_READY);
// or do_command_done() ?
do_controller_status(__func__);
do_controller_status(false, __func__);
}
void RL11_c::clear_errors() {
@@ -328,6 +345,7 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg,
// on drive select:
// move status of new drive to controller status register
// on command: signal worker thread
switch (device_reg->index) {
case 0: { // CS
if (unibus_control == UNIBUS_CONTROL_DATO) {
@@ -359,7 +377,7 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg,
// accept only command if controller ready
if (new_controller_ready) {
// GO not set
do_controller_status(__func__); // UNIBUS sees still "controller ready"
do_controller_status(false, __func__); // UNIBUS sees still "controller ready"
} else {
RL0102_c *drive; // some funct need the selected drive
bool execute_function_delayed;
@@ -415,7 +433,7 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg,
// signal worker() with pthread condition variable
// transition from high priority "unibusadapter thread" to
// standard "device thread".
do_controller_status(__func__); // UNIBUS sees now "controller not ready"
do_controller_status(false, __func__); // UNIBUS sees now "controller not ready"
// wake up worker()
pthread_cond_signal(&on_after_register_access_cond);
}
@@ -451,12 +469,6 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg,
// now SSYN goes inactive !
}
bool RL11_c::on_param_changed(parameter_c *param) {
UNUSED(param) ;
return true ;
}
void RL11_c::on_power_changed(void) {
// storagecontroller_c forwards to drives
storagecontroller_c::on_power_changed();
@@ -484,15 +496,19 @@ void RL11_c::on_drive_status_changed(storagedrive_c *drive) {
if (drive->unitno.value != selected_drive_unitno)
return;
// show status lines in CS for selected drive
do_controller_status(__func__);
do_controller_status(false, __func__);
}
// issue interrupt.
// do not set CONTROLLER READY bit
void RL11_c::do_command_done(void) {
bool do_int = false;
// bool do_int = false;
if (interrupt_enable && state != RL11_STATE_CONTROLLER_READY)
do_int = true;
change_state_INTR(RL11_STATE_CONTROLLER_READY);
else
// no intr
change_state(RL11_STATE_CONTROLLER_READY);
#ifdef OLD
if (do_int) {
// first set visible "controller ready"
@@ -501,20 +517,16 @@ void RL11_c::do_command_done(void) {
* RDY interrupt too late and at wrong program position
*/
// worker_boost_realtime_priority();
// SET_DEBUG_PIN1(1)
;
change_state(RL11_STATE_CONTROLLER_READY);
// scheduler may inject time here, if called from low prio worker() !
// pending interrupt triggered
interrupt();
//TODO: connect to interrupt register busreg_CS
unibusadapter->INTR(intr_request, NULL, 0);
DEBUG("Interrupt!");
// SET_DEBUG_PIN1(0)
;
// worker_restore_realtime_priority();
} else
// no intr
change_state(RL11_STATE_CONTROLLER_READY);
// no intr
change_state(RL11_STATE_CONTROLLER_READY);
/*
{
// interrupt on leading edge of "controller-ready" signal
@@ -524,28 +536,30 @@ void RL11_c::do_command_done(void) {
do_controller_status(__func__);
change_state(RL11_STATE_CONTROLLER_READY);
*/
#endif
}
// CS read/Write access different registers.
// write current status into CS, for next read operation
// must be done after each DATO
void RL11_c::do_controller_status(const char *debug_info) {
void RL11_c::do_controller_status(bool do_intr, const char *debug_info) {
RL0102_c *drive = selected_drive();
uint16_t tmp = 0;
bool drive_error_any = drive->drive_error_line; // save, may change
bool controller_ready = (state == RL11_STATE_CONTROLLER_READY);
//bit 0: drive ready
if (drive->drive_ready_line)
tmp |= 0x0001;
tmp |= BIT(0);
// bits <1:3>: function code
tmp |= (function_code << 1);
// bits <4:5>: bus_address <17:16>
tmp |= (unibus_address_msb & 3) << 4;
// bit 6: IE
if (interrupt_enable)
tmp |= (1 << 6);
tmp |= BIT(6);
// bit 7: CRDY
if (state == RL11_STATE_CONTROLLER_READY)
tmp |= (1 << 7);
if (controller_ready)
tmp |= BIT(7);
// bits <8:9>: drive select
tmp |= (selected_drive_unitno << 8);
// bit <10:13>: error code. Only some possible errors
@@ -559,15 +573,21 @@ void RL11_c::do_controller_status(const char *debug_info) {
tmp |= (0x08) << 10; // error code "NXM" = 1000
// bit 14 is drive error
if (drive_error_any)
tmp |= (1 << 14);
tmp |= BIT(14);
// bit 15 is composite error
if (error_dma_timeout || error_operation_incomplete || error_writecheck
|| error_header_not_found || drive_error_any) {
tmp |= (1 << 15);
tmp |= BIT(15);
}
set_register_dati_value(busreg_CS, tmp, debug_info);
// now visible om UNIBUS
if (do_intr) {
// set CSR atomically with INTR signal lines
assert(interrupt_enable);
assert(controller_ready);
unibusadapter->INTR(intr_request, busreg_CS, tmp);
} else
set_register_dati_value(busreg_CS, tmp, debug_info);
// now visible on UNIBUS
}
@@ -579,7 +599,6 @@ void RL11_c::do_operation_incomplete(const char *info) {
timeout.wait_ms(200 / emulation_speed.value);
error_operation_incomplete = true;
do_command_done();
}
// separate proc, to have a testpoint
@@ -587,7 +606,14 @@ void RL11_c::change_state(unsigned new_state) {
if (state != new_state)
DEBUG("Change RL11 state from 0x%x to 0x%x.", state, new_state);
state = new_state;
do_controller_status(__func__);
do_controller_status(false, __func__);
}
void RL11_c::change_state_INTR(unsigned new_state) {
if (state != new_state)
DEBUG("Change RL11 state from 0x%x to 0x%x.", state, new_state);
state = new_state;
do_controller_status(true, __func__);
}
// start seek operation, then interrupt
@@ -718,26 +744,32 @@ void RL11_c::state_readwrite() {
//logger.debug_hexdump(LC_RL, "Read data between disk access and DMA",
// (uint8_t *) silo, sizeof(silo), NULL);
// start DMA transmission of SILO into memory
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATO, unibus_address, silo,
dma_wordcount, &unibus_address);
unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, unibus_address, silo,
dma_wordcount);
error_dma_timeout = !dma_request.success;
unibus_address = dma_request.unibus_end_addr;
} else if (function_code == CMD_WRITE_CHECK) {
// read sector data to compare with sector data
drive->cmd_read_next_sector_data(silo, 128);
// logger.debug_hexdump(LC_RL, "Read data between disk access and DMA",
// (uint8_t *) silo, sizeof(silo), NULL);
// start DMA transmission of memory to compare with SILO
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI, unibus_address, silo_compare,
dma_wordcount, &unibus_address);
unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, unibus_address,
silo_compare, dma_wordcount);
error_dma_timeout = !dma_request.success;
unibus_address = dma_request.unibus_end_addr;
} else if (function_code == CMD_WRITE_DATA) {
// start DMA transmission of memory into SILO
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI, unibus_address, silo,
dma_wordcount, &unibus_address);
unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, unibus_address, silo,
dma_wordcount);
error_dma_timeout = !dma_request.success;
unibus_address = dma_request.unibus_end_addr;
}
// request_client_DMA() was blocking, DMA processed now.
// unibus_address updated to last accesses address
unibus_address += 2; // was last address, is now next to fill
// if timeout: addr AFTER illegal address (verified)
// request_client_DMA() was blocking, DMA processed now.
// unibus_address updated to last accesses address
unibus_address += 2; // was last address, is now next to fill
// if timeout: yes, current addr is addr AFTER illegal address (verified)
update_unibus_address(unibus_address); // set addr msb to cs
if (error_dma_timeout) {
@@ -796,13 +828,14 @@ void RL11_c::state_readwrite() {
// thread
// excutes commands
void RL11_c::worker(void) {
void RL11_c::worker(unsigned instance) {
UNUSED(instance); // only one
assert(!pthread_mutex_lock(&on_after_register_access_mutex));
// set prio to RT, but less than unibus_adapter
worker_init_realtime_priority(rt_device);
while (!worker_terminate) {
while (!workers_terminate) {
/* process command state machine in parallel with
"active register" state changes
*/
@@ -810,11 +843,15 @@ void RL11_c::worker(void) {
// wait for "cmd" signal of on_after_register_access()
int res;
// CRDY in busreg_CS->active_dati_flipflops & 0x80))
// may still be inactive, when PRU updatesit with iNTR delayed.
// enable operation of pending on_after_register_access()
/*
if (!(busreg_CS->active_dati_flipflops & 0x80)) { // CRDY must be set
ERROR("CRDY not set, CS=%06o", busreg_CS->active_dati_flipflops);
logger->dump(logger->default_filepath);
}
*/
res = pthread_cond_wait(&on_after_register_access_cond,
&on_after_register_access_mutex);
if (res != 0) {

View File

@@ -1,40 +1,41 @@
/* rl11.hpp: Implementation of the RL11 controller
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
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:
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 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.
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
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _RL11_HPP_
#define _RL11_HPP_
#include "unibusadapter.hpp"
#include "storagecontroller.hpp"
class RL0102_c ;
class RL0102_c;
class RL11_c: public storagecontroller_c {
private:
/** everything sharead between different threads must be "volatile" */
/** everything shared between different threads must be "volatile" */
volatile unsigned state; // one of RL11_STATE_*
timeout_c timeout ;
timeout_c timeout;
// which drive will communicate with the controeller via the drive bus.
volatile uint8_t selected_drive_unitno;
@@ -47,8 +48,8 @@ private:
volatile bool error_operation_incomplete; // OPI. operation aborted because of error
volatile bool error_dma_timeout; // DMA operation addresses non existing memory
volatile bool error_writecheck ; // mismatch between memory and disk data
volatile bool error_header_not_found ; // sector address notfound on track
volatile bool error_writecheck; // mismatch between memory and disk data
volatile bool error_header_not_found; // sector address notfound on track
// after "read header": MP register show different values on successive reads
volatile uint16_t mpr_silo[3]; // max 3 word deep buffer
@@ -58,6 +59,10 @@ private:
uint16_t silo[128]; // buffer from/to drive
uint16_t silo_compare[128]; // memory data to be compared with silo
// RL11 has one INTR and DMA
dma_request_c dma_request = dma_request_c(this); // operated by unibusadapter
intr_request_c intr_request = intr_request_c(this);
// only 16*16 = 256 byte buffer from drive (SILO)
// one DMA transaction per sector, must be complete within one sector time
// (2400 rpm = 40 rounds per second -> 1/160 sec per sector = 6,25 millisecs
@@ -66,24 +71,27 @@ private:
void update_unibus_address(uint32_t addr);
uint16_t get_MP_wordcount(void);
void set_MP_wordcount(uint16_t wordcount);
void set_MP_dati_value(uint16_t w, const char *debug_info) ;
void set_MP_dati_silo(const char *debug_info) ;
void set_MP_dati_value(uint16_t w, const char *debug_info);
void set_MP_dati_silo(const char *debug_info);
void clear_errors(void) ;
void clear_errors(void);
// controller can accept commands again
void do_command_done(void);
// set readable value for busreg_CS
void do_controller_status(const char *debug_info);
void do_controller_status(bool do_intr, const char *debug_info);
void do_operation_incomplete(const char *info);
// state machines
void change_state(unsigned new_state) ;
void change_state(unsigned new_state);
void change_state_INTR(unsigned new_state);
void state_seek(void);
void state_readwrite(void);
void connect_to_panel(void);
void disconnect_from_panel(void);
void refresh_params_from_panel(void);
public:
// register interface to RL11 controller
@@ -95,27 +103,23 @@ public:
RL11_c(void);
~RL11_c(void);
bool on_param_changed(parameter_c *param) override;
void reset(void);
void connect_to_panel(void) ;
void disconnect_from_panel(void) ;
void refresh_params_from_panel(void) ;
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
RL0102_c *selected_drive(void) ;
RL0102_c *selected_drive(void);
// called by unibusadapter after DATI/DATO access to active emulated register
// Runs at 100% RT priority, UNIBUS is stopped by SSYN while this is running.
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
void on_drive_status_changed(storagedrive_c *drive) ;
void on_drive_status_changed(storagedrive_c *drive);
};
#endif

View File

@@ -0,0 +1,498 @@
/*
***************************************************************************
*
* Author: Teunis van Beelen
*
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen
*
* Email: teuniz@gmail.com
*
***************************************************************************
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************
*/
/* 2019, June: made C++, added parity/frame/BREAK option, Joerg Hoppe */
/* Last revision Teunis van Beelen: November 22, 2017 */
/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */
#include "rs232.hpp"
rs232_c::rs232_c() {
CharTransmissionTime_us = 0;
}
// devname without leading "/dev/"
// returns 0 on success, else error
int rs232_c::OpenComport(const char *devname, int baudrate, const char *mode,
bool par_and_break) {
char full_devname[256];
int baudr;
int status;
strcpy(full_devname, "/dev/");
strncat(full_devname, devname, 255);
full_devname[255] = 0;
switch (baudrate) {
case 50:
baudr = B50;
break;
case 75:
baudr = B75;
break;
case 110:
baudr = B110;
break;
case 134:
baudr = B134;
break;
case 150:
baudr = B150;
break;
case 200:
baudr = B200;
break;
case 300:
baudr = B300;
break;
case 600:
baudr = B600;
break;
case 1200:
baudr = B1200;
break;
case 1800:
baudr = B1800;
break;
case 2400:
baudr = B2400;
break;
case 4800:
baudr = B4800;
break;
case 9600:
baudr = B9600;
break;
case 19200:
baudr = B19200;
break;
case 38400:
baudr = B38400;
break;
case 57600:
baudr = B57600;
break;
case 115200:
baudr = B115200;
break;
case 230400:
baudr = B230400;
break;
case 460800:
baudr = B460800;
break;
case 500000:
baudr = B500000;
break;
case 576000:
baudr = B576000;
break;
case 921600:
baudr = B921600;
break;
case 1000000:
baudr = B1000000;
break;
case 1152000:
baudr = B1152000;
break;
case 1500000:
baudr = B1500000;
break;
case 2000000:
baudr = B2000000;
break;
case 2500000:
baudr = B2500000;
break;
case 3000000:
baudr = B3000000;
break;
case 3500000:
baudr = B3500000;
break;
case 4000000:
baudr = B4000000;
break;
default:
printf("invalid baudrate\n");
return (1);
break;
}
int cbits = CS8;
int cpar = 0;
int ipar = IGNPAR;
int bstop = 0;
if (strlen(mode) != 3) {
printf("invalid mode \"%s\"\n", mode);
return (1);
}
unsigned bitcount = 1; // start bit
switch (mode[0]) {
case '8':
cbits = CS8;
bitcount += 8;
break;
case '7':
cbits = CS7;
bitcount += 7;
break;
case '6':
cbits = CS6;
bitcount += 6;
break;
case '5':
cbits = CS5;
bitcount += 5;
break;
default:
printf("invalid number of data-bits '%c'\n", mode[0]);
return (1);
break;
}
switch (mode[1]) {
case 'N':
case 'n':
cpar = 0;
ipar = IGNPAR;
break;
case 'E':
case 'e':
cpar = PARENB;
ipar = INPCK;
bitcount += 1;
break;
case 'O':
case 'o':
cpar = (PARENB | PARODD);
ipar = INPCK;
bitcount += 1;
break;
default:
printf("invalid parity '%c'\n", mode[1]);
return (1);
break;
}
switch (mode[2]) {
case '1':
bstop = 0;
bitcount += 1;
break;
case '2':
bstop = CSTOPB;
bitcount += 2;
break;
default:
printf("invalid number of stop bits '%c'\n", mode[2]);
return (1);
break;
}
// bit count is 10 for 8N1
// Calc time to transmit on character
CharTransmissionTime_us = (1000000 * bitcount) / baudrate;
/* scan for BREAK and frame/parity errors?
To read BREAK not as \0:
PARMRK=1 and parity checking -> BREAK violates frame pattern -> is recieved as \377 \0 \0
*/
int iflag;
if (par_and_break)
iflag = PARMRK | INPCK;
else
iflag = ipar;
/*
http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
http://man7.org/linux/man-pages/man3/termios.3.html
*/
Cport = open(full_devname, O_RDWR | O_NOCTTY | O_NDELAY);
if (Cport == -1) {
perror("unable to open comport ");
return (1);
}
/* lock access so that another process can't also use the port */
if (flock(Cport, LOCK_EX | LOCK_NB) != 0) {
close(Cport);
perror("Another process has locked the comport.");
return (1);
}
error = tcgetattr(Cport, &old_port_settings);
if (error == -1) {
close(Cport);
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
perror("unable to read portsettings ");
return (1);
}
memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */
new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD;
new_port_settings.c_iflag = iflag;
new_port_settings.c_oflag = 0;
new_port_settings.c_lflag = 0;
new_port_settings.c_cc[VMIN] = 0; /* block until n bytes are received */
new_port_settings.c_cc[VTIME] = 0; /* block until a timer expires (n * 100 mSec.) */
cfsetispeed(&new_port_settings, baudr);
cfsetospeed(&new_port_settings, baudr);
error = tcsetattr(Cport, TCSANOW, &new_port_settings);
if (error == -1) {
tcsetattr(Cport, TCSANOW, &old_port_settings);
close(Cport);
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
perror("unable to adjust portsettings ");
return (1);
}
/* http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */
if (ioctl(Cport, TIOCMGET, &status) == -1) {
tcsetattr(Cport, TCSANOW, &old_port_settings);
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
perror("unable to get portstatus");
return (1);
}
status |= TIOCM_DTR; /* turn on DTR */
status |= TIOCM_RTS; /* turn on RTS */
if (ioctl(Cport, TIOCMSET, &status) == -1) {
tcsetattr(Cport, TCSANOW, &old_port_settings);
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
perror("unable to set portstatus");
return (1);
}
return (0);
}
int rs232_c::PollComport(unsigned char *buf, int size) {
int n;
n = read(Cport, buf, size);
if (n < 0) {
if (errno == EAGAIN)
return 0;
}
return (n);
}
int rs232_c::SendByte(unsigned char byte) {
int n = write(Cport, &byte, 1);
if (n < 0) {
if (errno == EAGAIN) {
return 0;
} else {
return 1;
}
}
return (0);
}
int rs232_c::SendBuf(unsigned char *buf, int size) {
int n = write(Cport, buf, size);
if (n < 0) {
if (errno == EAGAIN) {
return 0;
} else {
return -1;
}
}
return (n);
}
// put byte in to rcv queue
void rs232_c::LoopbackByte(unsigned char byte) {
if (ioctl(Cport, TIOCSTI, &byte) == -1) {
perror("unable to insert byte into input queue");
}
}
void rs232_c::SetBreak(int break_state) {
if (ioctl(Cport, break_state ? TIOCSBRK : TIOCCBRK) == -1) {
perror("unable to set break status");
}
}
void rs232_c::CloseComport(void) {
int status;
CharTransmissionTime_us = 0;
if (ioctl(Cport, TIOCMGET, &status) == -1) {
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
status &= ~TIOCM_RTS; /* turn off RTS */
if (ioctl(Cport, TIOCMSET, &status) == -1) {
perror("unable to set portstatus");
}
tcsetattr(Cport, TCSANOW, &old_port_settings);
close(Cport);
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
}
/*
Constant Description
TIOCM_LE DSR (data set ready/line enable)
TIOCM_DTR DTR (data terminal ready)
TIOCM_RTS RTS (request to send)
TIOCM_ST Secondary TXD (transmit)
TIOCM_SR Secondary RXD (receive)
TIOCM_CTS CTS (clear to send)
TIOCM_CAR DCD (data carrier detect)
TIOCM_CD see TIOCM_CAR
TIOCM_RNG RNG (ring)
TIOCM_RI see TIOCM_RNG
TIOCM_DSR DSR (data set ready)
http://man7.org/linux/man-pages/man4/tty_ioctl.4.html
*/
int rs232_c::IsDCDEnabled(void) {
int status;
ioctl(Cport, TIOCMGET, &status);
if (status & TIOCM_CAR)
return (1);
else
return (0);
}
int rs232_c::IsCTSEnabled(void) {
int status;
ioctl(Cport, TIOCMGET, &status);
if (status & TIOCM_CTS)
return (1);
else
return (0);
}
int rs232_c::IsDSREnabled(void) {
int status;
ioctl(Cport, TIOCMGET, &status);
if (status & TIOCM_DSR)
return (1);
else
return (0);
}
void rs232_c::enableDTR(void) {
int status;
if (ioctl(Cport, TIOCMGET, &status) == -1) {
perror("unable to get portstatus");
}
status |= TIOCM_DTR; /* turn on DTR */
if (ioctl(Cport, TIOCMSET, &status) == -1) {
perror("unable to set portstatus");
}
}
void rs232_c::disableDTR(void) {
int status;
if (ioctl(Cport, TIOCMGET, &status) == -1) {
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
if (ioctl(Cport, TIOCMSET, &status) == -1) {
perror("unable to set portstatus");
}
}
void rs232_c::enableRTS(void) {
int status;
if (ioctl(Cport, TIOCMGET, &status) == -1) {
perror("unable to get portstatus");
}
status |= TIOCM_RTS; /* turn on RTS */
if (ioctl(Cport, TIOCMSET, &status) == -1) {
perror("unable to set portstatus");
}
}
void rs232_c::disableRTS(void) {
int status;
if (ioctl(Cport, TIOCMGET, &status) == -1) {
perror("unable to get portstatus");
}
status &= ~TIOCM_RTS; /* turn off RTS */
if (ioctl(Cport, TIOCMSET, &status) == -1) {
perror("unable to set portstatus");
}
}
void rs232_c::flushRX(void) {
tcflush(Cport, TCIFLUSH);
}
void rs232_c::flushTX(void) {
tcflush(Cport, TCOFLUSH);
}
void rs232_c::flushRXTX(void) {
tcflush(Cport, TCIOFLUSH);
}
void rs232_c::cputs(const char *text) /* sends a string to serial port */
{
while (*text != 0)
SendByte(*(text++));
}

View File

@@ -0,0 +1,90 @@
/*
***************************************************************************
*
* Author: Teunis van Beelen
*
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen
*
* Email: teuniz@gmail.com
*
***************************************************************************
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************
*/
/* Last revision: August 5, 2017 */
/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */
#ifndef rs232_INCLUDED
#define rs232_INCLUDED
#include <stdio.h>
#include <string.h>
#if defined(__linux__) || defined(__FreeBSD__)
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <sys/file.h>
#include <errno.h>
#else
#include <windows.h>
#endif
#define RS232_PORTNR 38
class rs232_c {
private:
int Cport; // file handle of COM port
int error;
struct termios new_port_settings, old_port_settings;
public:
rs232_c();
unsigned CharTransmissionTime_us;
int OpenComport(const char *devname, int baudrate, const char *mode, bool par_and_break);
int PollComport(unsigned char *buf, int size);
int SendByte(unsigned char byte);
void LoopbackByte(unsigned char byte);
int SendBuf(unsigned char *buf, int size);
void SetBreak(int break_state);
void CloseComport(void);
void cputs(const char *);
int IsDCDEnabled(void);
int IsCTSEnabled(void);
int IsDSREnabled(void);
void enableDTR(void);
void disableDTR(void);
void enableRTS(void);
void disableRTS(void);
void flushRX(void);
void flushTX(void);
void flushRXTX(void);
};
#endif

View File

@@ -0,0 +1,157 @@
/* rs232adapter.cpp: route byte xmt/rcv interface to stream and RS232
Copyright (c) 2019, 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.
8-aug-2019 JH edit start
This is a stream router.
- Main interface is a byte-port with sent/poll functions
- the bytestream can be routed to a RS232 object for TxD and RxD
- the bytestream can be routed to two streams: rcv/xmt
- for the xmt stream a pattern matcher is implemented, which search for strigns in the stream
To be used to router DL11 RCV/XMT ports to RS232 and/or program functions
. stream_rcv stream_xmt upper end "STREAM" .
. \ / / \ .
. | | .
. | +---> ringbuffer "PATTERN" .
. | | .
. | loopback | .
. rcv <----------|---< byte_loopback() .
. decoder | .
. buffer | .
. | | .
. +-----<--------|---< rs232.Poll()---< RxD "RS232" .
. | | .
. | +---> rs232.Send()---> TxD "RS232" .
. | | .
. \ / / \ .
. byte_rcv_poll() byte_xmt_send() lower end "BYTE" .
. .
. DL11 RCVR DL11 XMT DL11 .
. DATI DATO UNIBUS .
*/
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include "rs232adapter.hpp"
rs232adapter_c::rs232adapter_c() {
log_label = "ADP232";
rs232 = NULL;
stream_rcv = NULL;
stream_xmt = NULL;
rcv_termios_error_encoding = false;
rcv_decoder.clear();
pattern_stream_data[0] = 0;
pattern[0] = 0;
pattern_found = false;
baudrate = 0; // default: no delay
rcv_baudrate_delay.start_us(0); // start with elapsed() == true"
}
// BYTE interface: check for received char (from stream or RS232)
// Attention: must produce 0xff 0 sequences for termios encoded byte errors
// and 0xff 0xff for \ff
// If IGNPAR=0, PARMRK=1: error on <char> received as \377 \0 <char>
// \377 received as \377 \377
bool rs232adapter_c::byte_rcv_poll(unsigned char *rcvchar) {
pthread_mutex_lock(&mutex);
bool result = false;
// mixing input streams, with RS232 priority
// loopback or part of previous 0xff,0xff sequence ?
int c = rcv_decoder.get();
if (c != EOF) {
*rcvchar = c;
result = true;
}
if (!result && rs232) {
// rs2323 must be programmed to generate 0xff 0xff sequences
result = rs232->PollComport(rcvchar, 1);
}
if (stream_rcv && !result) {
// deliver next char from stream delayed, with simulated baudrate
if (baudrate == 0 || rcv_baudrate_delay.reached()) {
int c = stream_rcv->get();
if (c != EOF) {
*rcvchar = c;
if (rcv_termios_error_encoding && c == 0xff) {
// mark 2nd 0xff for output on next call
rcv_decoder.clear();
rcv_decoder.put(0xff);
}
result = true;
if (baudrate != 0)
rcv_baudrate_delay.start_us(10 * MILLION / baudrate); // assume 10 bits per char
}
}
}
pthread_mutex_unlock(&mutex);
return result;
}
void rs232adapter_c::byte_xmt_send(unsigned char xmtchar) {
if (rs232)
rs232->SendByte(xmtchar);
if (stream_xmt)
stream_xmt->put(xmtchar);
// pattern ring buffer
unsigned n = strlen(pattern);
if (n) {
// put new chars at end of string
unsigned m = strlen(pattern_stream_data);
assert(m < pattern_max_len);
pattern_stream_data[m] = xmtchar;
pattern_stream_data[m + 1] = 0;
// only keep the last chars in buffer.
while ((m = strlen(pattern_stream_data)) > n)
// strip first char, should loop only once
memmove(pattern_stream_data, pattern_stream_data + 1, m);
if (strstr(pattern_stream_data, pattern))
pattern_found = true; // user must clear
}
// pattern_buffer.
}
void rs232adapter_c::byte_loopback(unsigned char xmtchar) {
// not a queue, only single char (DL11 loopback)
rcv_decoder.clear();
rcv_decoder.put(xmtchar);
if (rcv_termios_error_encoding && xmtchar == 0xff)
rcv_decoder.put(0xff);
// fill intermediate buffer with seqeunce to receive
}
void rs232adapter_c::set_pattern(char *pattern) {
strncpy(this->pattern, pattern, pattern_max_len);
pattern_found = false;
pattern_stream_data[0] = 0;
}

View File

@@ -0,0 +1,86 @@
/* rs232adapter.hpp: route byte xmt/rcv interface to stream and RS232
Copyright (c) 2019, 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.
8-aug-2019 JH edit start
*/
#ifndef _RS232ADAPTER_HPP_
#define _RS232ADAPTER_HPP_
#include <ostream>
#include <istream>
#include <sstream>
#include "utils.hpp"
#include "logsource.hpp"
#include "rs232.hpp"
class rs232adapter_c: public logsource_c {
private:
// for loopback and to decode 0xff to 0xff,0xff
std::stringstream rcv_decoder;
// last sequence of xmt data for pattern matching
static const int pattern_max_len = 256 ;
char pattern[pattern_max_len+1]; // if != "", this is search for
char pattern_stream_data[pattern_max_len+1];
// deliver rcv chars delayed by this "baudrate"
timeout_c rcv_baudrate_delay ;
public:
rs232adapter_c();
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
unsigned baudrate ; // deliver rcv chars throttled by this "baudrate"
// if true, an inject 0xff is delivered as 0xff,0xff.
// this is compatible with termios(3) encoding of error flags
// If IGNPAR=0, PARMRK=1: error on <char> received as \377 \0 <char>
// \377 received as \377 \377
bool rcv_termios_error_encoding;
/*** RS232 interface ***/
rs232_c *rs232; // if assigned, routing to initialized RS232 port
/*** BYTE interface ***/
bool byte_rcv_poll(unsigned char *rcvchar);
void byte_xmt_send(unsigned char xmtchar);
void byte_loopback(unsigned char xmtchar);
/*** STREAM interface ***/
std::istream *stream_rcv; // users sets this to a stream which producess chars
// may be a stringstream to inject characters
std::ostream *stream_xmt; // users sets this to a stream in which
// chars are written to be transferred.
// may be "cout", or an stringstream
/*** PATTERN detection ***/
void set_pattern(char *pattern);
bool pattern_found; // switches ture on match, user must clear
};
#endif // _RS232ADAPTER_HPP_

View File

@@ -0,0 +1,238 @@
/* testcontroller.cpp: sample UNIBUS controller with selftest logic
Copyright (c) 2018-2019, 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.
23-jul-2019 JH added interrupt and DMA functions
12-nov-2018 JH entered beta phase
"Tester" is a device to test the event, stress INTR and DMA, and
implements 32 registers at start of IOpage
Controller registers:
------------------------
32 registers @ 0760200.. 0760276
all registers are marked as "active":
DATI and DATO are routed via events into the controller logic
UNIBUS is stopped with long SSYN
+0 = CSR: write command, read = status
other registers have no functions, are simple memory cells.
Backplane Slots: 10,11,12
DMA
---
has 2 DMA channels on different priority_slots
Used for priroity test of parallel DMA req with different slot priority
INTR
---
4 x 3 interrupts (BR4,5,6,7 at slot 10,11,12)
To test slot priority and level priroity
raise all simulataneously with CPU level = 7
-> no INTR triggered
lower CPU lvel to 6 -> 2 INTR in increasing slot priority triggered
Test #1 DMA priority test
------------------------
Write of 1 into CSR triggers test
First a long 1K DMA "A" with lower slot priority is started (DEPOSIT)
2nd a DMA "B" with higher slot priority is started
After some A-chunks, B get priorized and is completed earlier, despit started later.
Verify: At mem start, "B" values re found (B later),
at mem end "A" values are found (runs later)
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
//#include <iostream>
#include "utils.hpp"
#include "logger.hpp"
#include "unibus.h"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp" // definition of class device_c
#include "testcontroller.hpp"
testcontroller_c::testcontroller_c() :
unibusdevice_c() // super class constructor
{
unsigned i;
// static config
name.value = "Test controller";
type_name.value = "testcontroller_c";
log_label = "tc";
// mem at 160000: RT11 crashes?
set_default_bus_params(0760200, 16, 0, 0); // base addr, priority_slot, intr-vector, intr level
//
register_count = 32; // up to 760200 .. 760276
// CSR
CSR = &(this->registers[0]);
strcpy(CSR->name, "CSR");
CSR->active_on_dati = true; // controller state change on read
CSR->active_on_dato = true; // writing changes controller state
CSR->reset_value = 0;
CSR->writable_bits = 0xffff; // all registers are memory cells
// Other registers are "active": receive "on_after_register_access"
for (i = 1; i < this->register_count; i++) {
unibusdevice_register_t *reg = &(this->registers[i]);
sprintf(reg->name, "reg%02o", i); // name is register offset: "reg07"
reg->active_on_dati = true; // controller state change on read
reg->active_on_dato = true; // writing changes controller state
reg->reset_value = 0;
reg->writable_bits = 0xffff; // all registers are memory cells
}
// create DMA requests.
for (unsigned i = 0; i < dma_channel_count; i++) {
dma_request_c *dmareq = dma_channel_request[i] = new dma_request_c(this);
// lowest index = highest slot priority
dmareq->set_priority_slot(i + 15);
dma_channel_buffer[i] = new memoryimage_c();
}
// create INTR requests.
for (unsigned slot = 1; slot < PRIORITY_SLOT_COUNT; slot++)
for (unsigned level_index = 0; level_index < 4; level_index++) {
intr_request_c *intreq = intr_request[slot][level_index] = new intr_request_c(this);
intreq->set_priority_slot(slot);
intreq->set_level(level_index+4);
// "vector" uninitialized, must be set on use!
}
// dynamic state
access_count.value = 0;
}
testcontroller_c::~testcontroller_c() {
for (unsigned i = 0; i < dma_channel_count; i++) {
delete dma_channel_request[i];
delete dma_channel_buffer[i];
}
for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++)
for (unsigned level_index = 0; level_index < 4; level_index++)
delete intr_request[slot][level_index];
}
bool testcontroller_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// 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 testcontroller_c::on_after_register_access(unibusdevice_register_t *device_reg,
uint8_t unibus_control) {
// emulate a plain memory cell: written values can be read unchanged
if (unibus_control == UNIBUS_CONTROL_DATI) {
}
if (unibus_control == UNIBUS_CONTROL_DATO)
set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__);
if (device_reg == CSR) {
/// CSR has been written: signal worker()
pthread_cond_signal(&on_after_register_access_cond);
// worker now executes CSR command and clears datao lfipflops
}
// this is also called for some DATIs, no action anyhow.
access_count.value++;
// DEBUG writes to disk & console ... measured delay up to 30ms !
//DEBUG(LL_DEBUG, LC_demo_regs, "[%6u] reg +%d @ %06o %s", accesscount, (int ) device_reg->index,
// device_reg->addr, unibus_c::control2text(unibus_control));
}
void testcontroller_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
}
// UNIBUS INIT: clear all registers
void testcontroller_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("testcontroller_c::on_init()");
}
}
// background worker.
// Just print a heart beat
void testcontroller_c::worker(unsigned instance) {
UNUSED(instance); // only one
timeout_c timeout;
assert(!pthread_mutex_lock(&on_after_register_access_mutex));
// set prio to RT, but less than unibus_adapter
worker_init_realtime_priority(rt_device);
while (!workers_terminate) {
int res = pthread_cond_wait(&on_after_register_access_cond,
&on_after_register_access_mutex);
if (res != 0) {
ERROR("testcontroller_c::worker() pthread_cond_wait = %d = %s>", res,
strerror(res));
continue;
}
// execute command in CSR,
uint16_t cmd = CSR->active_dato_flipflops;
// mark as processed
CSR->active_dato_flipflops = 0;
set_register_dati_value(CSR, 0, __func__); // no status
switch (cmd) {
case 1:
test_dma_priority();
break;
}
timeout.wait_ms(1000);
cout << ".";
}
assert(!pthread_mutex_unlock(&on_after_register_access_mutex));
}
void testcontroller_c::test_dma_priority() {
}

View File

@@ -1,6 +1,6 @@
/* demo_regs.hpp: sample UNIBUS controller with register logic
/* testcontroller.hpp: sample UNIBUS controller with selftest logic
Copyright (c) 2018, Joerg Hoppe
Copyright (c) 2018-2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
@@ -21,36 +21,53 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23-jul-2019 JH added interrupt and DMA functions
12-nov-2018 JH entered beta phase
*/
#ifndef _DEMO_REGS_HPP_
#define _DEMO_REGS_HPP_
#ifndef _TESTCONTROLLER_HPP_
#define _TESTCONTROLLER_HPP_
#include "utils.hpp"
#include "unibusdevice.hpp"
#include "memoryimage.hpp"
#include "parameter.hpp"
class demo_regs_c: public unibusdevice_c {
class testcontroller_c: public unibusdevice_c {
private:
public:
parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count",
"ac",/*readonly*/
true, "", "%u", "Total # of register accesses", 32, 10);
unibusdevice_register_t *CSR; // command and status register
demo_regs_c();
parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count", "ac",/*readonly*/
true, "", "%u", "Total # of register accesses", 32, 10);
// For arbitray tests of the priority request system, we have
// one reqiest for every slot/level combination
static const unsigned dma_channel_count = 2;
dma_request_c *dma_channel_request[dma_channel_count];
// for concurrent DMA, testcontroller needs one data buffer per possible DMA.
// these are n*4MB !
memoryimage_c *dma_channel_buffer[dma_channel_count];
intr_request_c *intr_request[PRIORITY_SLOT_COUNT][4]; // 3 slots, 4 levels
testcontroller_c();
~testcontroller_c();
bool on_param_changed(parameter_c *param) override; // must implement
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
void test_dma_priority(void);
};
#endif

View File

@@ -46,9 +46,12 @@ uda_c::uda_c() :
type_name.value = "UDA50";
log_label = "uda";
default_base_addr = 0772150;
default_intr_vector = 0154;
default_intr_level = 5;
// base addr, intr-vector, intr level
set_default_bus_params(0772150, 20, 0154, 5) ;
dma_request.set_priority_slot(default_priority_slot) ;
intr_request.set_priority_slot(default_priority_slot) ;
intr_request.set_level(default_intr_level) ;
intr_request.set_vector(default_intr_vector) ;
// The UDA50 controller has two registers.
register_count = 2;
@@ -94,6 +97,12 @@ uda_c::~uda_c()
storagedrives.clear();
}
bool uda_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
}
//
// Reset():
// Resets the UDA controller state.
@@ -158,13 +167,15 @@ void uda_c::StateTransition(
// worker():
// Implements the initialization state machine.
//
void uda_c::worker(void)
void uda_c::worker(unsigned instance)
{
UNUSED(instance) ; // only one
worker_init_realtime_priority(rt_device);
timeout_c timeout;
while (!worker_terminate)
while (!workers_terminate)
{
//
// Wait to be awoken.
@@ -801,7 +812,7 @@ uda_c::Interrupt(void)
{
if ((_interruptEnable || _initStep == InitializationStep::Complete) && _interruptVector != 0)
{
interrupt();
unibusadapter->INTR(intr_request, NULL, 0); // todo: link to interupt register
}
}
@@ -934,11 +945,12 @@ uda_c::DMAWrite(
assert ((lengthInBytes % 2) == 0);
assert (address < 0x40000);
return unibusadapter->request_client_DMA(
unibusadapter->DMA(dma_request, true,
UNIBUS_CONTROL_DATO,
address,
reinterpret_cast<uint16_t*>(buffer),
lengthInBytes >> 1, NULL);
lengthInBytes >> 1);
return dma_request.success ;
}
//
@@ -965,13 +977,13 @@ uda_c::DMARead(
memset(reinterpret_cast<uint8_t*>(buffer), 0xc3, bufferSize);
bool success = unibusadapter->request_client_DMA(
unibusadapter->DMA(dma_request, true,
UNIBUS_CONTROL_DATI,
address,
buffer,
lengthInBytes >> 1, NULL);
lengthInBytes >> 1);
if (success)
if (dma_request.success)
{
return reinterpret_cast<uint8_t*>(buffer);
}

View File

@@ -9,6 +9,7 @@
#include <memory>
#include "utils.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
#include "storagecontroller.hpp"
#include "mscp_server.hpp"
@@ -61,7 +62,9 @@ public:
uda_c();
virtual ~uda_c();
void worker(void) override;
bool on_param_changed(parameter_c *param) override;
void worker(unsigned instance) override;
void on_after_register_access(
unibusdevice_register_t *device_reg,
@@ -71,6 +74,12 @@ public:
void on_init_changed(void) override;
void on_drive_status_changed(storagedrive_c *drive) override;
// As every storage controller UDA has one INTR and DMA
dma_request_c dma_request = dma_request_c(this) ; // operated by unibusadapter
intr_request_c intr_request = intr_request_c(this) ;
public:
//

Binary file not shown.

View File

@@ -0,0 +1,73 @@
# "demo" cmd Testsequence for DL11W
dc # "demo" menu: device without cpu
sd dl11
.print Test of DL11-W, without CPU.
.print Put UniBone in emtpy backplane.
.print Connect serial terminal emulator to UniBone "UART2", set to 9600 8N1
.input
en dl11
INIT
E
.print expected:
.print EXAM reg #0 RCSR 777560 -> 000000
.print EXAM reg #1 RBUF 777562 -> 000000
.print EXAM reg #2 XCSR 777564 -> 000200
.print EXAM reg #3 XBUF 777566 -> 000000
. print Send chars "Hello". Fast, chars may get lost
d xbuf 110
d xbuf 145
d xbuf 154
d xbuf 154
d xbuf 157
d xbuf 15
d xbuf 12
.print Now press an "a" on terminal keyboard
.input
e rcsr
e rbuf
e rcsr
.print Expected: rcsr = 200, rbuf = 141, rcsr = 000 (reading rbuf cleared "RCV_DONE")
.print Now press a "yz" on terminal keyboard
.input
e rcsr
e rbuf
e rcsr
.print Expected: rcsr = 200, rbuf = 14172 ("z"), rcsr = 000
.print switch terminal to 7NO. press "c"
e rbuf
.print Expected: rbuf = 373 (c +parity)
.print Check BREAK receive:
.print Set terminal to 300 baud, send a space (0x20) -> looots of 00s
e rbuf
.print Expected: a "0" with framing error: 170000
.print Set terminal back to 9600 baud
.input
.print Maintenance self receive
d xcsr 4 # MAINT ON
d xbuf 123 # sent "S"
e
.print Expected: rcsr = 200, rbuf = 123 (sent char "S" received back)

View File

@@ -0,0 +1,33 @@
# "demo" cmd Testsequence for DL11W
d # "demo" menu: device with cpu
sd dl11
.print Test of DL11-W, with PDP-11 CPU.
.print Connect serial terminal emulator to UniBone "UART2", set to 9600 8N1
.input
en dl11
en kw11
pwr
.wait 1000
.print must see Bootloader prompt on UART2 now!
m i
en rl
en rl0
sd rl0
p emulation_speed 10
p runstopbutton 0 # released: "LOAD"
p powerswitch 1 # power on, now in "load" state
p image xxdp25.rl02 # mount image file
p runstopbutton 1
m lp /root/10.02_devices/3_test/dl11w/ZDLDI0.BIN
.print Now start ZDLD at address 200
.print Or boot XXDP from DL, then execute "R ZDLDI0"
.print Set switch register with "D 176"
.print 100000 = HALT on error, 2000=error flags, 400= test BREAK, 100=disable KW11
# d 176 100000 # HALT on error

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More