mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-04-25 03:35:42 +00:00
Enable devices individually over param "enabled"
UNIBUS addr, intr vector, level setable
This commit is contained in:
@@ -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>
|
||||
@@ -68,17 +68,17 @@ static void device_worker_pthread_cleanup_handler(void *context) {
|
||||
|
||||
static void *device_worker_pthread_wrapper(void *context) {
|
||||
device_c *device = (device_c *) context;
|
||||
int oldstate ; // not used
|
||||
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!
|
||||
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
|
||||
pthread_cleanup_push(device_worker_pthread_cleanup_handler, device);
|
||||
device->worker();
|
||||
pthread_cleanup_pop(1); // call cleanup_handler on regular exit
|
||||
// not reached on pthread_cancel()
|
||||
#undef this
|
||||
return NULL;
|
||||
@@ -99,10 +99,13 @@ device_c::device_c() {
|
||||
// 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 +128,27 @@ device_c::~device_c() {
|
||||
mydevices.erase(p);
|
||||
}
|
||||
|
||||
bool device_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &enabled) {
|
||||
if (enabled.new_value)
|
||||
worker_start();
|
||||
else
|
||||
worker_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
|
||||
@@ -262,12 +285,13 @@ void device_c::worker_start(void) {
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
// pthread_attr_setstacksize(&attr, 1024*1024);
|
||||
assert(worker_terminated); // do not srtat device worker twiche in parallel!
|
||||
int status = pthread_create(&worker_pthread, &attr, &device_worker_pthread_wrapper,
|
||||
(void *) this);
|
||||
if (status != 0) {
|
||||
FATAL("Failed to create pthread with status = %d", status);
|
||||
}
|
||||
pthread_attr_destroy(&attr) ; // why?
|
||||
pthread_attr_destroy(&attr); // why?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +311,8 @@ void device_c::worker_stop(void) {
|
||||
// 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);
|
||||
FATAL("Failed to send cancellation request to worker_pthread with status = %d",
|
||||
status);
|
||||
}
|
||||
|
||||
// !! If crosscompling: this causes a crash in the worker thread
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -40,70 +40,74 @@ using namespace std;
|
||||
// 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 worker_start(void);
|
||||
void worker_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
|
||||
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();
|
||||
virtual ~device_c(); // class with virtual functions should have virtual destructors
|
||||
|
||||
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()
|
||||
|
||||
|
||||
pthread_t worker_pthread;
|
||||
void worker_start(void);
|
||||
void worker_stop(void);
|
||||
virtual void worker(void) = 0; // background worker function
|
||||
};
|
||||
|
||||
|
||||
@@ -73,16 +73,22 @@ 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() {
|
||||
@@ -96,7 +102,19 @@ parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name,
|
||||
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 +130,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 +149,18 @@ 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)
|
||||
@@ -146,10 +173,7 @@ void parameter_unsigned_c::parse(string text) {
|
||||
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 +192,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)
|
||||
@@ -180,10 +214,7 @@ void parameter_unsigned64_c::parse(string text) {
|
||||
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() {
|
||||
|
||||
@@ -93,25 +93,26 @@ 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();
|
||||
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();
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(bool new_value) ;
|
||||
};
|
||||
|
||||
class parameter_unsigned_c: public parameter_c {
|
||||
@@ -123,12 +124,13 @@ 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);
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(unsigned new_value) ;
|
||||
};
|
||||
|
||||
class parameter_unsigned64_c: public parameter_c {
|
||||
@@ -140,12 +142,13 @@ 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);
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(uint64_t new_value) ;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -67,47 +67,29 @@ using namespace std;
|
||||
#include "iopageregister.h"
|
||||
#include "unibusadapter.hpp"
|
||||
|
||||
dma_request_c::dma_request_c(
|
||||
uint8_t unibus_control,
|
||||
uint32_t unibus_addr,
|
||||
uint16_t* buffer,
|
||||
uint32_t wordcount) :
|
||||
_unibus_control(unibus_control),
|
||||
_unibus_start_addr(unibus_addr),
|
||||
_unibus_end_addr(0),
|
||||
_buffer(buffer),
|
||||
_wordcount(wordcount),
|
||||
_isComplete(false),
|
||||
_success(false)
|
||||
{
|
||||
dma_request_c::dma_request_c(uint8_t unibus_control, uint32_t unibus_addr, uint16_t* buffer,
|
||||
uint32_t wordcount) :
|
||||
_unibus_control(unibus_control), _unibus_start_addr(unibus_addr), _unibus_end_addr(0), _buffer(
|
||||
buffer), _wordcount(wordcount), _isComplete(false), _success(false) {
|
||||
|
||||
}
|
||||
|
||||
dma_request_c::~dma_request_c()
|
||||
{
|
||||
dma_request_c::~dma_request_c() {
|
||||
|
||||
}
|
||||
|
||||
irq_request_c::irq_request_c(
|
||||
unsigned level,
|
||||
unsigned vector) :
|
||||
_level(level),
|
||||
_vector(vector),
|
||||
_isComplete(false)
|
||||
{
|
||||
irq_request_c::irq_request_c(unsigned level, unsigned vector) :
|
||||
_level(level), _vector(vector), _isComplete(false) {
|
||||
|
||||
}
|
||||
|
||||
irq_request_c::~irq_request_c()
|
||||
{
|
||||
irq_request_c::~irq_request_c() {
|
||||
|
||||
}
|
||||
|
||||
void* bus_worker(
|
||||
void *context)
|
||||
{
|
||||
void* bus_worker(void *context) {
|
||||
unibusadapter_c* bus = reinterpret_cast<unibusadapter_c*>(context);
|
||||
bus->dma_worker();
|
||||
bus->dma_worker();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -115,11 +97,8 @@ unibusadapter_c *unibusadapter; // another Singleton
|
||||
// is registered in device_c.list<devices> ... order of static constructor calls ???
|
||||
|
||||
unibusadapter_c::unibusadapter_c() :
|
||||
device_c(),
|
||||
_busWakeup_cond(PTHREAD_COND_INITIALIZER),
|
||||
_requestFinished_cond(PTHREAD_COND_INITIALIZER),
|
||||
_busWorker_mutex(PTHREAD_MUTEX_INITIALIZER)
|
||||
{
|
||||
device_c(), _busWakeup_cond(PTHREAD_COND_INITIALIZER), _requestFinished_cond(
|
||||
PTHREAD_COND_INITIALIZER), _busWorker_mutex(PTHREAD_MUTEX_INITIALIZER) {
|
||||
unsigned i;
|
||||
log_label = "UNAPT";
|
||||
|
||||
@@ -137,26 +116,20 @@ unibusadapter_c::unibusadapter_c() :
|
||||
pthread_attr_t attribs;
|
||||
pthread_attr_init(&attribs);
|
||||
|
||||
int status = pthread_create(
|
||||
&_busWorker_pthread,
|
||||
&attribs,
|
||||
&bus_worker,
|
||||
reinterpret_cast<void*>(this));
|
||||
int status = pthread_create(&_busWorker_pthread, &attribs, &bus_worker,
|
||||
reinterpret_cast<void*>(this));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
if (status != 0) {
|
||||
FATAL("Failed to start unibus worker thread. Status 0x%x", status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool unibusadapter_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param);
|
||||
return true ;
|
||||
// no own parameter or "enable" logic
|
||||
return device_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
void unibusadapter_c::on_power_changed(void)
|
||||
{
|
||||
void unibusadapter_c::on_power_changed(void) {
|
||||
|
||||
}
|
||||
|
||||
@@ -175,9 +148,8 @@ void unibusadapter_c::worker_init_event() {
|
||||
device->on_init_changed();
|
||||
}
|
||||
|
||||
|
||||
// Clear bus request queues
|
||||
rundown_bus_requests();
|
||||
// Clear bus request queues
|
||||
rundown_bus_requests();
|
||||
}
|
||||
|
||||
void unibusadapter_c::worker_power_event() {
|
||||
@@ -410,7 +382,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
|
||||
for (i = 0; i < device.register_count; i++) {
|
||||
unibusdevice_register_t *device_reg = &(device.registers[i]);
|
||||
device_reg->addr = device.base_addr.value + 2 * i;
|
||||
if ( IOPAGE_REGISTER_ENTRY(*deviceregisters,device_reg->addr) != 0 )
|
||||
if ( IOPAGE_REGISTER_ENTRY(*deviceregisters,device_reg->addr)!= 0 )
|
||||
FATAL("IO page address conflict: %s implements register at %06o, belongs already to other device.",
|
||||
device.name.value.c_str(), device_reg->addr);
|
||||
}
|
||||
@@ -544,27 +516,19 @@ bool unibusadapter_c::request_INTR_active(const char *error_info) {
|
||||
// unibus_control = UNIBUS_CONTROL_DATI or _DATO
|
||||
// unibus_end_addr = last accessed address (success or timeout) and timeout condition
|
||||
// result: false on UNIBUS timeout
|
||||
bool unibusadapter_c::request_client_DMA(
|
||||
uint8_t unibus_control,
|
||||
uint32_t unibus_addr,
|
||||
uint16_t *buffer,
|
||||
uint32_t wordcount,
|
||||
uint32_t *unibus_end_addr) {
|
||||
bool unibusadapter_c::request_client_DMA(uint8_t unibus_control, uint32_t unibus_addr,
|
||||
uint16_t *buffer, uint32_t wordcount, uint32_t *unibus_end_addr) {
|
||||
|
||||
//
|
||||
// Acquire bus mutex; append new request to queue.
|
||||
// bus worker will wake and service the request in due time.
|
||||
//
|
||||
dma_request_c request(
|
||||
unibus_control,
|
||||
unibus_addr,
|
||||
buffer,
|
||||
wordcount);
|
||||
dma_request_c request(unibus_control, unibus_addr, buffer, wordcount);
|
||||
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_dmaRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_dmaRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
DEBUG("DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr,
|
||||
wordcount);
|
||||
@@ -573,59 +537,48 @@ bool unibusadapter_c::request_client_DMA(
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete())
|
||||
{
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
while (!request.IsComplete()) {
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
if (unibus_end_addr)
|
||||
*unibus_end_addr = request.GetUnibusEndAddr() ;
|
||||
*unibus_end_addr = request.GetUnibusEndAddr();
|
||||
|
||||
return request.GetSuccess() ;
|
||||
return request.GetSuccess();
|
||||
}
|
||||
|
||||
void unibusadapter_c::dma_worker()
|
||||
{
|
||||
void unibusadapter_c::dma_worker() {
|
||||
//worker_init_realtime_priority(rt_device);
|
||||
while(true)
|
||||
{
|
||||
while (true) {
|
||||
dma_request_c* dmaReq = nullptr;
|
||||
irq_request_c* irqReq = nullptr;
|
||||
|
||||
irq_request_c* irqReq = nullptr;
|
||||
|
||||
//
|
||||
// Wait for the next request.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while(_dmaRequests.empty() && _irqRequests.empty())
|
||||
{
|
||||
pthread_cond_wait(
|
||||
&_busWakeup_cond,
|
||||
&_busWorker_mutex);
|
||||
while (_dmaRequests.empty() && _irqRequests.empty()) {
|
||||
pthread_cond_wait(&_busWakeup_cond, &_busWorker_mutex);
|
||||
}
|
||||
|
||||
//
|
||||
// We have a request: prioritize IRQ over DMA, dequeue from the requisite
|
||||
// queue and get to work.
|
||||
//
|
||||
if (!_irqRequests.empty())
|
||||
{
|
||||
if (!_irqRequests.empty()) {
|
||||
irqReq = _irqRequests.front();
|
||||
_irqRequests.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
dmaReq = _dmaRequests.front();
|
||||
} else {
|
||||
dmaReq = _dmaRequests.front();
|
||||
_dmaRequests.pop();
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
|
||||
// Sanity check: Should be no active DMA or interrupt requests on the PRU.
|
||||
assert (!request_DMA_active(nullptr) && !request_INTR_active(nullptr));
|
||||
assert(!request_DMA_active(nullptr) && !request_INTR_active(nullptr));
|
||||
|
||||
if (dmaReq)
|
||||
{
|
||||
if (dmaReq) {
|
||||
// We do the DMA transfer in chunks so we can handle arbitrary buffer sizes.
|
||||
// (the PRU mailbox has limited space available.)
|
||||
// Configure the DMA transfer.
|
||||
@@ -636,22 +589,17 @@ void unibusadapter_c::dma_worker()
|
||||
uint32_t unibusAddr = dmaReq->GetUnibusStartAddr();
|
||||
uint32_t bufferOffset = 0;
|
||||
|
||||
|
||||
while (wordCount > 0)
|
||||
{
|
||||
while (wordCount > 0) {
|
||||
uint32_t chunkSize = std::min(maxTransferSize, wordCount);
|
||||
|
||||
mailbox->dma.startaddr = unibusAddr + bufferOffset * 2;
|
||||
mailbox->dma.control = dmaReq->GetUnibusControl();
|
||||
mailbox->dma.wordcount = chunkSize;
|
||||
mailbox->dma.wordcount = chunkSize;
|
||||
|
||||
// Copy outgoing data into maibox DMA buffer
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO)
|
||||
{
|
||||
memcpy(
|
||||
(void*)mailbox->dma.words,
|
||||
dmaReq->GetBuffer() + bufferOffset,
|
||||
2 * chunkSize);
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO) {
|
||||
memcpy((void*) mailbox->dma.words, dmaReq->GetBuffer() + bufferOffset,
|
||||
2 * chunkSize);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -662,11 +610,10 @@ void unibusadapter_c::dma_worker()
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to do this?
|
||||
timeout_c timeout;
|
||||
int retries = 0;
|
||||
while (request_DMA_active(nullptr) && retries < 10000)
|
||||
{
|
||||
int retries = 0;
|
||||
while (request_DMA_active(nullptr) && retries < 10000) {
|
||||
timeout.wait_us(50);
|
||||
retries++;
|
||||
retries++;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -677,18 +624,14 @@ void unibusadapter_c::dma_worker()
|
||||
// Nothing to do in that case but give up.
|
||||
// And log the issue. Should get to the root of this..
|
||||
//
|
||||
if (retries == 10000)
|
||||
{
|
||||
if (retries == 10000) {
|
||||
ERROR("dma timeout");
|
||||
}
|
||||
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATI)
|
||||
{
|
||||
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATI) {
|
||||
// Copy data read from mailbox to user's buffer.
|
||||
memcpy(
|
||||
dmaReq->GetBuffer() + bufferOffset,
|
||||
(void *)mailbox->dma.words,
|
||||
2 * chunkSize);
|
||||
memcpy(dmaReq->GetBuffer() + bufferOffset, (void *) mailbox->dma.words,
|
||||
2 * chunkSize);
|
||||
}
|
||||
wordCount -= chunkSize;
|
||||
bufferOffset += chunkSize;
|
||||
@@ -698,8 +641,9 @@ void unibusadapter_c::dma_worker()
|
||||
dmaReq->SetSuccess(mailbox->dma.cur_status == DMA_STATE_READY);
|
||||
// no success: UnibusEndAddr is first failed address
|
||||
|
||||
assert(dmaReq->GetUnibusStartAddr() + dmaReq->GetWordCount() * 2 ==
|
||||
mailbox->dma.cur_addr + 2);
|
||||
assert(
|
||||
dmaReq->GetUnibusStartAddr() + dmaReq->GetWordCount() * 2
|
||||
== mailbox->dma.cur_addr + 2);
|
||||
|
||||
//
|
||||
// Signal that the request is complete.
|
||||
@@ -708,30 +652,28 @@ void unibusadapter_c::dma_worker()
|
||||
dmaReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Handle interrupt request
|
||||
switch(irqReq->GetInterruptLevel())
|
||||
{
|
||||
case 4:
|
||||
switch (irqReq->GetInterruptLevel()) {
|
||||
case 4:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 5:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 6:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
|
||||
case 7:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR("Request_INTR(): Illegal priority %u, aborting", irqReq->GetInterruptLevel());
|
||||
|
||||
default:
|
||||
ERROR("Request_INTR(): Illegal priority %u, aborting",
|
||||
irqReq->GetInterruptLevel());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -747,68 +689,60 @@ void unibusadapter_c::dma_worker()
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to
|
||||
// do this? (as w/dma)
|
||||
timeout_c timeout;
|
||||
while(request_INTR_active(nullptr))
|
||||
{
|
||||
timeout.wait_us(50);
|
||||
}
|
||||
}
|
||||
timeout_c timeout;
|
||||
while (request_INTR_active(nullptr)) {
|
||||
timeout.wait_us(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unibusadapter_c::rundown_bus_requests()
|
||||
{
|
||||
void unibusadapter_c::rundown_bus_requests() {
|
||||
//
|
||||
// Cancel all pending DMA and IRQ requests, freeing threads waiting
|
||||
// on completion.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!_dmaRequests.empty())
|
||||
{
|
||||
while (!_dmaRequests.empty()) {
|
||||
dma_request_c* dmaReq = _dmaRequests.front();
|
||||
dmaReq->SetSuccess(false);
|
||||
dmaReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
_dmaRequests.pop();
|
||||
}
|
||||
while (!_irqRequests.empty())
|
||||
{
|
||||
while (!_irqRequests.empty()) {
|
||||
irq_request_c* irqReq = _irqRequests.front();
|
||||
irqReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
_irqRequests.pop();
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void unibusadapter_c::request_INTR(uint32_t level, uint32_t vector) {
|
||||
//
|
||||
// Acquire bus mutex; append new request to queue.
|
||||
// bus worker will wake and service the request in due time.
|
||||
//
|
||||
irq_request_c request(
|
||||
level,
|
||||
vector);
|
||||
//
|
||||
// Acquire bus mutex; append new request to queue.
|
||||
// bus worker will wake and service the request in due time.
|
||||
//
|
||||
irq_request_c request(level, vector);
|
||||
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_irqRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_irqRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
//
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete())
|
||||
{
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
//
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete()) {
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
//
|
||||
// And we're done.
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
/* 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, 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 _UNIBUSADAPTER_HPP_
|
||||
@@ -33,63 +33,83 @@
|
||||
#include "iopageregister.h"
|
||||
#include "unibusdevice.hpp"
|
||||
|
||||
class dma_request_c
|
||||
{
|
||||
class dma_request_c {
|
||||
public:
|
||||
dma_request_c(
|
||||
uint8_t unibus_control,
|
||||
uint32_t unibus_addr,
|
||||
uint16_t *buffer,
|
||||
uint32_t wordcount);
|
||||
dma_request_c(uint8_t unibus_control, uint32_t unibus_addr, uint16_t *buffer,
|
||||
uint32_t wordcount);
|
||||
|
||||
~dma_request_c();
|
||||
~dma_request_c();
|
||||
|
||||
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; }
|
||||
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; }
|
||||
bool IsComplete() {
|
||||
return _isComplete;
|
||||
}
|
||||
bool GetSuccess() {
|
||||
return _success;
|
||||
}
|
||||
|
||||
void SetComplete() { _isComplete = true; }
|
||||
void SetSuccess(bool success) { _success = 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;
|
||||
uint8_t _unibus_control;
|
||||
uint32_t _unibus_start_addr;
|
||||
uint32_t _unibus_end_addr;
|
||||
uint16_t* _buffer;
|
||||
uint32_t _wordcount;
|
||||
|
||||
bool _isComplete;
|
||||
bool _success;
|
||||
bool _isComplete;
|
||||
bool _success;
|
||||
};
|
||||
|
||||
class irq_request_c
|
||||
{
|
||||
class irq_request_c {
|
||||
public:
|
||||
irq_request_c(
|
||||
uint32_t level,
|
||||
uint32_t vector);
|
||||
irq_request_c(uint32_t level, uint32_t vector);
|
||||
|
||||
~irq_request_c();
|
||||
~irq_request_c();
|
||||
|
||||
uint32_t GetInterruptLevel() { return _level; }
|
||||
uint32_t GetVector() { return _vector; }
|
||||
bool IsComplete() { return _isComplete; }
|
||||
uint32_t GetInterruptLevel() {
|
||||
return _level;
|
||||
}
|
||||
uint32_t GetVector() {
|
||||
return _vector;
|
||||
}
|
||||
bool IsComplete() {
|
||||
return _isComplete;
|
||||
}
|
||||
|
||||
void SetComplete() { _isComplete = true; }
|
||||
void SetComplete() {
|
||||
_isComplete = true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t _level;
|
||||
uint32_t _vector;
|
||||
bool _isComplete;
|
||||
uint32_t _level;
|
||||
uint32_t _vector;
|
||||
bool _isComplete;
|
||||
};
|
||||
|
||||
|
||||
// is a device_c. need a thread (but no params)
|
||||
class unibusadapter_c: public device_c {
|
||||
@@ -97,45 +117,46 @@ class unibusadapter_c: public device_c {
|
||||
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_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
|
||||
|
||||
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_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);
|
||||
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);
|
||||
void rundown_bus_requests(void);
|
||||
|
||||
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;
|
||||
|
||||
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,34 +1,34 @@
|
||||
/* 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 "logger.hpp"
|
||||
@@ -39,19 +39,51 @@ 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_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;
|
||||
intr_vector.readonly = true;
|
||||
intr_level.readonly = true;
|
||||
install(); // visible on UNIBUS
|
||||
} else {
|
||||
// disable
|
||||
uninstall();
|
||||
base_addr.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 default_intr_vector, unsigned default_intr_level) {
|
||||
this->default_base_addr = this->base_addr.value = default_base_addr;
|
||||
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 +94,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);
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -38,7 +38,7 @@ class unibusdevice_c;
|
||||
typedef 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 +77,33 @@ typedef struct {
|
||||
} unibusdevice_register_t;
|
||||
|
||||
class unibusdevice_c: public device_c {
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
// DEC defaults as defined by device type
|
||||
uint32_t default_base_addr;
|
||||
unsigned default_intr_vector;
|
||||
unsigned default_intr_level;
|
||||
void set_default_bus_params(uint32_t default_base_addr, 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 +111,28 @@ 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) ;
|
||||
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) ;
|
||||
void interrupt(void);
|
||||
|
||||
// callback to be called on controller register DATI/DATO events.
|
||||
// must ACK mailbox.event.signal. Asynchron!
|
||||
@@ -146,8 +146,7 @@ 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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user