1
0
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:
Joerg Hoppe
2019-06-20 21:58:04 +02:00
parent ccd6747892
commit 3952cb93b0
38 changed files with 1341 additions and 1427 deletions

View File

@@ -1,27 +1,27 @@
/* device.cpp - abstract base class for devices
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
12-nov-2018 JH entered beta phase
Abstract device, with or without UNIBUS registers.
maybe mass storage controller, storage drive or other UNIBUS device
@@ -32,7 +32,7 @@
- has a worker()
- has a logger
- has parameters
*/
*/
#define _DEVICE_CPP_
#include <string.h>
@@ -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

View File

@@ -1,28 +1,28 @@
/* device.hpp - abstract base class for devices
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _DEVICE_HPP_
#define _DEVICE_HPP_
@@ -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
};

View File

@@ -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() {

View File

@@ -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) ;
};

View File

@@ -1,30 +1,30 @@
/* storagecontroller.cpp: a unibus device with several "storagedrives" attached
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
12-nov-2018 JH entered beta phase
A unibus device with several "storagedrives"
supports the "attach" command
A unibus device with several "storagedrives"
supports the "attach" command
*/
#include "utils.hpp"
@@ -41,8 +41,15 @@ storagecontroller_c::~storagecontroller_c() {
// implements params, so must handle "change"
bool storagecontroller_c::on_param_changed(parameter_c *param) {
UNUSED(param) ;
return true ;
if (param == &enabled) {
if (!enabled.new_value)
// power/up/down attached drives, then plug to UNIBUS
// if disable, disable also the drives ("contreolelr plugged from UNIBUS)")
// on enable, leave them disabled (user may decide which to use)
for (unsigned i = 0; i < drivecount; i++)
storagedrives[i]->enabled.set(false);
}
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// forward BUS events to connected storage drives
@@ -65,21 +72,3 @@ void storagecontroller_c::on_init_changed() {
}
}
// start/stop threads of this and all drives
void storagecontroller_c::worker_start() {
vector<storagedrive_c*>::iterator it;
for (it = storagedrives.begin(); it != storagedrives.end(); it++) {
(*it)->worker_start();
}
device_c::worker_start();
}
void storagecontroller_c::worker_stop() {
device_c::worker_stop();
vector<storagedrive_c*>::iterator it;
for (it = storagedrives.begin(); it != storagedrives.end(); it++) {
(*it)->worker_stop();
}
}

View File

@@ -1,27 +1,27 @@
/* storagecontroller.hpp: a unibus device with several "storagedrives" attached
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
12-nov-2018 JH entered beta phase
*/
@@ -41,15 +41,12 @@ public:
// does not instantiate the drives
storagecontroller_c(void);
virtual ~storagecontroller_c() ; // classes with virtual functions shoudl have virtual destructors
virtual ~storagecontroller_c(); // classes with virtual functions shoudl have virtual destructors
virtual bool on_param_changed(parameter_c *param) override;
virtual void on_power_changed() override ;
virtual void on_init_changed() override ;
virtual void on_drive_status_changed(storagedrive_c *drive) = 0 ;
void worker_start() ; // start/stop threads of this and all drives
void worker_stop() ;
virtual void on_power_changed() override;
virtual void on_init_changed() override;
virtual void on_drive_status_changed(storagedrive_c *drive) = 0;
};

View File

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

View File

@@ -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.

View File

@@ -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

View File

@@ -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);
}

View File

@@ -1,28 +1,28 @@
/* unibusdevice.hpp: abstract device with interface to unibusadapter
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
*/
12-nov-2018 JH entered beta phase
*/
#ifndef _UNIBUSDEVICE_HPP_
#define _UNIBUSDEVICE_HPP_
@@ -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);
};