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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
107
10.01_base/2_src/arm/priorityrequest.cpp
Normal file
107
10.01_base/2_src/arm/priorityrequest.cpp
Normal 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;
|
||||
}
|
||||
|
||||
183
10.01_base/2_src/arm/priorityrequest.hpp
Normal file
183
10.01_base/2_src/arm/priorityrequest.hpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -102,8 +102,6 @@ public:
|
||||
}
|
||||
virtual void on_init_changed(void) {
|
||||
}
|
||||
virtual void worker(void) {
|
||||
}
|
||||
|
||||
void test(void);
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) ;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
111
10.01_base/2_src/pru1/pru1_timeouts.c
Normal file
111
10.01_base/2_src/pru1/pru1_timeouts.c
Normal 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);
|
||||
}
|
||||
50
10.01_base/2_src/pru1/pru1_timeouts.h
Normal file
50
10.01_base/2_src/pru1/pru1_timeouts.h
Normal 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
|
||||
@@ -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 ;
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
185
10.01_base/3_test/intrtst.cmd
Normal file
185
10.01_base/3_test/intrtst.cmd
Normal 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!)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1322
10.01_base/3_test/intrtst.lst
Normal file
1322
10.01_base/3_test/intrtst.lst
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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.
BIN
10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf
Normal file
BIN
10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf
Normal file
Binary file not shown.
@@ -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()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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()");
|
||||
}
|
||||
}
|
||||
|
||||
638
10.02_devices/2_src/dl11w.cpp
Normal file
638
10.02_devices/2_src/dl11w.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
243
10.02_devices/2_src/dl11w.hpp
Normal file
243
10.02_devices/2_src/dl11w.hpp
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
5
10.02_devices/2_src/rk05.hpp
Executable file → Normal 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
498
10.02_devices/2_src/rs232.cpp
Normal file
498
10.02_devices/2_src/rs232.cpp
Normal 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++));
|
||||
}
|
||||
|
||||
90
10.02_devices/2_src/rs232.hpp
Normal file
90
10.02_devices/2_src/rs232.hpp
Normal 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
|
||||
|
||||
157
10.02_devices/2_src/rs232adapter.cpp
Normal file
157
10.02_devices/2_src/rs232adapter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
86
10.02_devices/2_src/rs232adapter.hpp
Normal file
86
10.02_devices/2_src/rs232adapter.hpp
Normal 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_
|
||||
|
||||
238
10.02_devices/2_src/testcontroller.cpp
Normal file
238
10.02_devices/2_src/testcontroller.cpp
Normal 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() {
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
//
|
||||
|
||||
BIN
10.02_devices/3_test/dl11w/ZDLDI0.BIN
Normal file
BIN
10.02_devices/3_test/dl11w/ZDLDI0.BIN
Normal file
Binary file not shown.
73
10.02_devices/3_test/dl11w/test.cmd
Normal file
73
10.02_devices/3_test/dl11w/test.cmd
Normal 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
33
10.02_devices/3_test/dl11w/zdld.cmd
Normal file
33
10.02_devices/3_test/dl11w/zdld.cmd
Normal 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
|
||||
BIN
10.02_devices/3_test/rl02/ZRLJB1.BIC
Normal file
BIN
10.02_devices/3_test/rl02/ZRLJB1.BIC
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user