diff --git a/10.01_base/2_src/arm/ddrmem.cpp b/10.01_base/2_src/arm/ddrmem.cpp index 80eaafc..d77b44a 100644 --- a/10.01_base/2_src/arm/ddrmem.cpp +++ b/10.01_base/2_src/arm/ddrmem.cpp @@ -1,28 +1,28 @@ /* ddrmem.cpp - Control the shared DDR RAM - used for UNIBUS memory - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #include #include @@ -42,10 +42,9 @@ ddrmem_c *ddrmem; ddrmem_c::ddrmem_c() { - log_label = "DDRMEM" ; + log_label = "DDRMEM"; } - // check allocated memory and print info void ddrmem_c::info() { INFO("Shared DDR memory: %u bytes available, %u bytes needed.", len, sizeof(ddrmem_t)); @@ -130,7 +129,7 @@ void ddrmem_c::fill_pattern(void) { void ddrmem_c::fill_pattern_pru(void) { // ddrmem_base_physical and _len already set assert((uint32_t )mailbox->ddrmem_base_physical == base_physical); - mailbox_execute(ARM2PRU_DDR_FILL_PATTERN, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_DDR_FILL_PATTERN); } // set corrected values for emulated memory range @@ -183,6 +182,6 @@ void ddrmem_c::unibus_slave(uint32_t startaddr, uint32_t endaddr) { s = inputline(buf, sizeof(buf), NULL); } while (strlen(s) == 0); // clearing arm2pru_req stops the emulation - mailbox_execute(ARM2PRU_NONE, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_NONE); } diff --git a/10.01_base/2_src/arm/device.cpp b/10.01_base/2_src/arm/device.cpp index a20b471..e7bcb1f 100644 --- a/10.01_base/2_src/arm/device.cpp +++ b/10.01_base/2_src/arm/device.cpp @@ -132,7 +132,7 @@ device_c::~device_c() { // only to be called in constructors void device_c::set_workers_count(unsigned workers_count) { workers.resize(workers_count); - for (unsigned instance=0; instance < workers_count; instance++) { + for (unsigned instance = 0; instance < workers_count; instance++) { device_worker_c *worker_instance = &workers[instance]; worker_instance->device = this; worker_instance->instance = instance; diff --git a/10.01_base/2_src/arm/device.hpp b/10.01_base/2_src/arm/device.hpp index 5019449..3f850fe 100644 --- a/10.01_base/2_src/arm/device.hpp +++ b/10.01_base/2_src/arm/device.hpp @@ -89,8 +89,8 @@ public: int worker_sched_priority; enum worker_priority_e { - none_rt, // under all RT priorities - rt_device, // all controeller and storage worker + none_rt, // lower than all RT priorities + rt_device, // all controller and storage worker rt_max // 100% CPU, uninterruptable }; void worker_init_realtime_priority(enum worker_priority_e priority); @@ -126,7 +126,7 @@ public: // default background worker function for devices without need. virtual void worker(unsigned instance) { UNUSED(instance); - printf("Warning: default device_c::worker() called, better use set_worker_count(0) ") ; + printf("Warning: default device_c::worker() called, better use set_worker_count(0) "); } }; diff --git a/10.01_base/2_src/arm/gpios.cpp b/10.01_base/2_src/arm/gpios.cpp index ae67409..5611b8b 100644 --- a/10.01_base/2_src/arm/gpios.cpp +++ b/10.01_base/2_src/arm/gpios.cpp @@ -616,7 +616,7 @@ void buslatches_register() { // PRU1 does it void buslatches_pru_reset() { assert(pru->prucode_id == pru_c::PRUCODE_TEST); - mailbox_execute(ARM2PRU_BUSLATCH_INIT, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_INIT); } // read the REG_DATIN[0..7] pins @@ -627,7 +627,7 @@ unsigned buslatches_getval(unsigned reg_sel) { while (mailbox->buslatch.addr != reg_sel) ; // cache ! - mailbox_execute(ARM2PRU_BUSLATCH_GET, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_GET); return mailbox->buslatch.val; // PRU1 has put the result here } @@ -639,7 +639,7 @@ void buslatches_setval(unsigned reg_sel, unsigned bitmask, unsigned val) { mailbox->buslatch.bitmask = bitmask & 0xff; mailbox->buslatch.val = val; - mailbox_execute(ARM2PRU_BUSLATCH_SET, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_SET); } // some pattern tests on a register latch @@ -849,7 +849,7 @@ void buslatches_test_simple_pattern_multi(unsigned pattern) { mailbox->buslatch_exerciser.pattern = (pass_no % MAILBOX_BUSLATCH_EXERCISER_PATTERN_COUNT); - mailbox_execute(ARM2PRU_BUSLATCH_EXERCISER, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_EXERCISER); // check: mailbox readvalues == write values ? for (unsigned i = 0; i < BUSLATCHES_COUNT; i++) { diff --git a/10.01_base/2_src/arm/gpios.hpp b/10.01_base/2_src/arm/gpios.hpp index 72eb6e9..f142a77 100644 --- a/10.01_base/2_src/arm/gpios.hpp +++ b/10.01_base/2_src/arm/gpios.hpp @@ -83,11 +83,11 @@ typedef struct { #define MAX_GPIOCOUNT 100 // test pins -// set 1 -> pin auf H -#define SET_DEBUG_PIN0(n) GPIO_SETVAL(gpios->led[0], !!(n)) -#define SET_DEBUG_PIN1(n) GPIO_SETVAL(gpios->led[1], !!(n)) -#define SET_DEBUG_PIN2(n) GPIO_SETVAL(gpios->led[2], !!(n)) -#define SET_DEBUG_PIN3(n) GPIO_SETVAL(gpios->led[3], !!(n)) +// SET(1) -> pin auf H, LED OFF +#define ARM_DEBUG_PIN0(n) GPIO_SETVAL(gpios->led[0], !!(n)) +#define ARM_DEBUG_PIN1(n) GPIO_SETVAL(gpios->led[1], !!(n)) +#define ARM_DEBUG_PIN2(n) GPIO_SETVAL(gpios->led[2], !!(n)) +#define ARM_DEBUG_PIN3(n) GPIO_SETVAL(gpios->led[3], !!(n)) class gpios_c: public logsource_c { private: @@ -117,7 +117,6 @@ public: void test_loopback(void); }; - #define BUSLATCHES_COUNT 8 // save current state uf gpios and registers, @@ -235,7 +234,7 @@ void buslatches_setval(unsigned reg_sel, unsigned bitmask, unsigned val); unsigned buslatches_getval(unsigned reg_sel); void buslatches_test_simple_pattern(unsigned pattern, unsigned reg_sel); -void buslatches_test_simple_pattern_multi( unsigned pattern); +void buslatches_test_simple_pattern_multi(unsigned pattern); void buslatches_test_timing(uint8_t addr_0_7, uint8_t addr_8_15, uint8_t data_0_7, uint8_t data_8_15); diff --git a/10.01_base/2_src/arm/iopageregister.cpp b/10.01_base/2_src/arm/iopageregister.cpp index 7162217..66aa80a 100644 --- a/10.01_base/2_src/arm/iopageregister.cpp +++ b/10.01_base/2_src/arm/iopageregister.cpp @@ -1,28 +1,28 @@ /* iopageregister.cpp: handle ARM-PRU shared struct with device register descriptors - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #define _IOPAGEREGISTER_CPP_ diff --git a/10.01_base/2_src/arm/mailbox.cpp b/10.01_base/2_src/arm/mailbox.cpp index 40c93f6..4e0d5fb 100644 --- a/10.01_base/2_src/arm/mailbox.cpp +++ b/10.01_base/2_src/arm/mailbox.cpp @@ -1,29 +1,28 @@ /* mailbox.cpp: datastructs common to ARM and PRU - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ - + 12-nov-2018 JH entered beta phase + */ #define _MAILBOX_CPP_ @@ -39,8 +38,7 @@ // is located in PRU 12kb shared memory. // address symbol "" fetched from linker map -volatile mailbox_t *mailbox; - +volatile mailbox_t *mailbox = NULL; // Init all fields, most to 0's int mailbox_connect(void) { @@ -57,7 +55,7 @@ int mailbox_connect(void) { // now ARM and PRU can access the mailbox - memset((void*)mailbox, 0, sizeof(mailbox_t)) ; + memset((void*) mailbox, 0, sizeof(mailbox_t)); // tell PRU location of shared DDR RAM mailbox->ddrmem_base_physical = (ddrmem_t *) ddrmem->base_physical; @@ -65,11 +63,9 @@ int mailbox_connect(void) { return 0; } - - void mailbox_print(void) { printf("INFO: Content of mailbox to PRU:\n" - "arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp); + "arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp); } /* simulate simple register accesses: @@ -103,21 +99,19 @@ void mailbox_test1() { } /* start cmd to PRU via mailbox. Wait until ready - * mailbox union members must have been filled + * mailbox union members must have been filled. */ -uint32_t xxx ; -void mailbox_execute(uint8_t request, uint8_t stopcode) { +uint32_t xxx; +void mailbox_execute(uint8_t request) { // write to arm2pru_req must be last memory operation __sync_synchronize(); + while (mailbox->arm2pru_req != ARM2PRU_NONE) + ; // wait to complete + mailbox->arm2pru_req = request; // go! do { - xxx = mailbox-> arm2pru_req ; -if (mailbox->events.eventmask) { - // event not processed? will hang DMA. -// printf("WARNING: Unprocessed mailbox.events.eventmask = 0x%x\n", (unsigned) mailbox->events.eventmask) ; -// mailbox->events.eventmask = 0 ; -} - } while (xxx != stopcode) ; - while (mailbox->arm2pru_req != stopcode) + xxx = mailbox->arm2pru_req; + } while (xxx != ARM2PRU_NONE); + while (mailbox->arm2pru_req != ARM2PRU_NONE) ; // wait until processed } diff --git a/10.01_base/2_src/arm/memoryimage.cpp b/10.01_base/2_src/arm/memoryimage.cpp index 0082f88..63ed502 100644 --- a/10.01_base/2_src/arm/memoryimage.cpp +++ b/10.01_base/2_src/arm/memoryimage.cpp @@ -2,37 +2,37 @@ Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - - Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 12-nov-2018 JH entered beta phase - 18-Jun-2017 JH created + 12-nov-2018 JH entered beta phase + 18-Jun-2017 JH created */ #define _MEMORYIMAGE_CPP_ @@ -132,7 +132,7 @@ bool memoryimage_c::load_binary(const char *fname) { unsigned wordidx, n; fin = fopen(fname, "rb"); if (!fin) { - printf("%s\n",fileErrorText("Error opening file %s for read", fname)); + printf("%s\n", fileErrorText("Error opening file %s for read", fname)); return false; } // try to read max address range, shorter files are OK @@ -149,7 +149,7 @@ void memoryimage_c::save_binary(const char *fname, unsigned bytecount) { unsigned n; fout = fopen(fname, "wb"); if (!fout) { - printf("%s\n",fileErrorText("Error opening file %s for write", fname)); + printf("%s\n", fileErrorText("Error opening file %s for write", fname)); return; } // try to read max address range, shorter files are OK @@ -204,7 +204,7 @@ bool memoryimage_c::load_addr_value_text(const char *fname) { fin = fopen(fname, "r"); if (!fin) { - printf("%s\n", fileErrorText("Error opening file %s for write", fname)) ; + printf("%s\n", fileErrorText("Error opening file %s for write", fname)); return false; } entry_address = MEMORY_ADDRESS_INVALID; // not known @@ -467,7 +467,7 @@ bool memoryimage_c::load_macro11_listing(const char *fname, const char *entrylab tokenidx = 0; // # of number processed ready = false; addr = 0; - line_addr = 0 ; + line_addr = 0; while (!ready) { while (*tp && isspace(*tp)) tp++; // skip white space @@ -538,7 +538,7 @@ bool memoryimage_c::load_papertape(const char *fname) { entry_address = MEMORY_ADDRESS_INVALID; // not yet known stream_byte_index = 0; - block_byte_size = addr = 0 ; // -Wmaybe-uninitialized + block_byte_size = addr = 0; // -Wmaybe-uninitialized while (!feof(fin)) { b = fgetc(fin); // ERROR("[0x%04x] state=%d b=0x%02x sum=0x%02x block_byte_idx=%d", diff --git a/10.01_base/2_src/arm/memoryimage.hpp b/10.01_base/2_src/arm/memoryimage.hpp index 3fd8502..2868aaf 100644 --- a/10.01_base/2_src/arm/memoryimage.hpp +++ b/10.01_base/2_src/arm/memoryimage.hpp @@ -2,37 +2,37 @@ Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - - Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 12-nov-2018 JH entered beta phase - 18-Jun-2017 JH created + 12-nov-2018 JH entered beta phase + 18-Jun-2017 JH created */ @@ -83,7 +83,7 @@ public: int entry_address; // start address, if found. MEMORY_ADDRESS_INVALID = invalid memoryimage_c() { - log_label = "MEMIMG" ; + log_label = "MEMIMG"; } void init(void); @@ -94,12 +94,11 @@ public: assert_address(addr); return valid[wordidx]; } - void fill(uint16_t fillword) ; + void fill(uint16_t fillword); void get_addr_range(unsigned *first, unsigned* last); unsigned get_word_count(void); - void set_addr_range(unsigned first, unsigned last) ; - + void set_addr_range(unsigned first, unsigned last); bool load_addr_value_text(const char *fname);bool load_macro11_listing(const char *fname, const char *entrylabel);bool load_papertape(const char *fname);bool load_binary( diff --git a/10.01_base/2_src/arm/parameter.cpp b/10.01_base/2_src/arm/parameter.cpp index 430a88e..8e862d0 100644 --- a/10.01_base/2_src/arm/parameter.cpp +++ b/10.01_base/2_src/arm/parameter.cpp @@ -32,9 +32,8 @@ #include "device.hpp" #define _PARAMETER_CPP_ - parameter_c::parameter_c(parameterized_c *parameterized, string name, string shortname, - bool readonly, string unit, string format, string info) { +bool readonly, string unit, string format, string info) { this->parameterized = parameterized; this->name = name; this->shortname = shortname; @@ -53,8 +52,7 @@ parameter_c::~parameter_c() { // to be implemented in subclass void parameter_c::parse(string text) { - throw bad_parameter_parse( - "parameter_c::parse(" + text + ") to be implemented in subclass"); + throw bad_parameter_parse("parameter_c::parse(" + text + ") to be implemented in subclass"); } // convert to text @@ -74,21 +72,20 @@ 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 ; + 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"); - set(text) ; + set(text); } string *parameter_string_c::render() { @@ -96,20 +93,19 @@ string *parameter_string_c::render() { return &printbuffer; } -parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name, string shortname, +parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name, + string shortname, bool readonly, string info) : parameter_c(parameterized, name, shortname, readonly, "", "", info) { value = false; } - - void parameter_bool_c::set(bool new_value) { if (value == new_value) - return ; // call "on_change" only on change - + return; // call "on_change" only on change + // reject parsed value, if device parameter check complains - this->new_value = new_value ; + this->new_value = new_value; if (parameterized == NULL || parameterized->on_param_changed(this)) value = new_value; } @@ -130,7 +126,7 @@ void parameter_bool_c::parse(string text) { new_value = false; else throw bad_parameter_parse("Illegal boolean expression \"" + text + "\""); - set(new_value) ; + set(new_value); } string *parameter_bool_c::render() { @@ -150,12 +146,11 @@ parameter_unsigned_c::parameter_unsigned_c(parameterized_c *parameterized, strin 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 ; + 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; @@ -168,12 +163,11 @@ void parameter_unsigned_c::parse(string text) { TRIM_STRING(text); new_value = strtol(text.c_str(), &endptr, base); if (*endptr) - throw bad_parameter_parse( - "Format error in \"" + text + "\" at \"" + *endptr + "\""); + throw bad_parameter_parse("Format error in \"" + text + "\" at \"" + *endptr + "\""); if (new_value & ~BitmaskFromLen32[bitwidth]) // throw bad_parameter_parse( "Number " + to_string(new_value) + " exceeds bitwidth " + to_string(bitwidth)); - set(new_value) ; + set(new_value); } string *parameter_unsigned_c::render() { @@ -194,9 +188,9 @@ parameter_unsigned64_c::parameter_unsigned64_c(parameterized_c *parameterized, s 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 ; + 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; @@ -209,12 +203,11 @@ void parameter_unsigned64_c::parse(string text) { TRIM_STRING(text); new_value = strtoll(text.c_str(), &endptr, base); if (*endptr) - throw bad_parameter_parse( - "Format error in \"" + text + "\" at \"" + *endptr + "\""); + throw bad_parameter_parse("Format error in \"" + text + "\" at \"" + *endptr + "\""); if (new_value & ~BitmaskFromLen64[bitwidth]) // throw bad_parameter_parse( "Number " + to_string(new_value) + " exceeds bitwidth " + to_string(bitwidth)); - set(new_value) ; + set(new_value); } string *parameter_unsigned64_c::render() { @@ -224,7 +217,6 @@ string *parameter_unsigned64_c::render() { return &printbuffer; } - // add reference to parameter. It will be automatically deleted parameter_c *parameterized_c::param_add(parameter_c *param) { parameter.push_back(param); @@ -247,4 +239,3 @@ parameter_c *parameterized_c::param_by_name(string name) { return NULL; } - diff --git a/10.01_base/2_src/arm/parameter.hpp b/10.01_base/2_src/arm/parameter.hpp index 6f1b9b3..8324bca 100644 --- a/10.01_base/2_src/arm/parameter.hpp +++ b/10.01_base/2_src/arm/parameter.hpp @@ -1,28 +1,28 @@ /* parameter.hpp: collection of typed name/value pairs, used by devices and other objects - Copyright (c) 2018-2019, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018-2019, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 16-mar-2019 JH unlinked from devices - 12-nov-2018 JH entered beta phase -*/ + 16-mar-2019 JH unlinked from devices + 12-nov-2018 JH entered beta phase + */ #ifndef _PARAMETER_HPP_ #define _PARAMETER_HPP_ @@ -33,43 +33,45 @@ #include using namespace std; - class bad_parameter: public exception { - private: - string message; - public: - bad_parameter(string message) { - (this->message = message); - } - const char* what() const noexcept { - return message.c_str(); - } -} ; +private: + string message; +public: + bad_parameter(string message) { + (this->message = message); + } + const char* what() const noexcept { + return message.c_str(); + } +}; class bad_parameter_parse: public bad_parameter { public: - bad_parameter_parse(string message): bad_parameter(message) { } + bad_parameter_parse(string message) : + bad_parameter(message) { + } }; class bad_parameter_check: public bad_parameter { public: - bad_parameter_check(string message): bad_parameter(message) { } + bad_parameter_check(string message) : + bad_parameter(message) { + } }; - class bad_parameter_readonly: public bad_parameter { public: - bad_parameter_readonly(string message): bad_parameter(message) { } + bad_parameter_readonly(string message) : + bad_parameter(message) { + } }; - - -class parameterized_c ; +class parameterized_c; class parameter_c { private: public: - parameterized_c *parameterized ; // link to parent object + parameterized_c *parameterized; // link to parent object string name; string shortname; bool readonly; @@ -78,7 +80,8 @@ public: string format; // printf, scanf // parameter_c(); - parameter_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string unit, string format, string info); + parameter_c(parameterized_c *parameterized, string name, string shortname, bool readonly, + string unit, string format, string info); virtual ~parameter_c(); // class with virtual functions should have virtual destructors // convert text to value. result: ok? @@ -88,18 +91,18 @@ public: virtual string *render(void); }; - class parameter_string_c: public parameter_c { public: // dynamic state string value; string new_value; - parameter_string_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string info); + parameter_string_c(parameterized_c *parameterized, string name, string shortname, + bool readonly, string info); ~parameter_string_c(); string *render(void) override; void parse(string text) override; - void set(string new_value) ; + void set(string new_value); }; class parameter_bool_c: public parameter_c { @@ -108,11 +111,12 @@ public: bool value; bool new_value; - parameter_bool_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string info); + parameter_bool_c(parameterized_c *parameterized, string name, string shortname, + bool readonly, string info); parameter_bool_c(); string *render(void) override; void parse(string text) override; - void set(bool new_value) ; + void set(bool new_value); }; class parameter_unsigned_c: public parameter_c { @@ -126,11 +130,12 @@ public: unsigned value; unsigned new_value; - parameter_unsigned_c(parameterized_c *parameterized, string name,string shortname, bool readonly, string unit, string format, - string info, unsigned bitwidth, unsigned base); + parameter_unsigned_c(parameterized_c *parameterized, string name, string shortname, + bool readonly, string unit, string format, string info, unsigned bitwidth, + unsigned base); string *render(void) override; void parse(string text) override; - void set(unsigned new_value) ; + void set(unsigned new_value); }; class parameter_unsigned64_c: public parameter_c { @@ -142,21 +147,21 @@ public: // dynamic state uint64_t value; - uint64_t new_value ; + uint64_t new_value; - parameter_unsigned64_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string unit, string format, - string info, unsigned bitwidth, unsigned base); + parameter_unsigned64_c(parameterized_c *parameterized, string name, string shortname, + bool readonly, string unit, string format, string info, unsigned bitwidth, + unsigned base); string *render(void) override; void parse(string text) override; - void set(uint64_t new_value) ; + void set(uint64_t new_value); }; - // objects with parameters are "parameterized" and inherit this. class parameterized_c { - public: - vector parameter; - +public: + vector parameter; + // register parameter parameter_c *param_add(parameter_c *param); @@ -164,11 +169,10 @@ class parameterized_c { parameter_c *param_by_name(string name); // sort? - + // called after param value changed. // result: false = "new_value" not excepted, error printed. - virtual bool on_param_changed(parameter_c *param) = 0 ; -} ; - + virtual bool on_param_changed(parameter_c *param) = 0; +}; #endif diff --git a/10.01_base/2_src/arm/pru.cpp b/10.01_base/2_src/arm/pru.cpp index e0b92db..6a6896b 100644 --- a/10.01_base/2_src/arm/pru.cpp +++ b/10.01_base/2_src/arm/pru.cpp @@ -134,7 +134,6 @@ int pru_c::start(enum prucode_enum prucode_id) { // use stop() before restart() assert(this->prucode_id == PRUCODE_NONE); - /* initialize PRU */ if ((rtn = prussdrv_init()) != 0) { ERROR("prussdrv_init() failed"); @@ -153,7 +152,6 @@ int pru_c::start(enum prucode_enum prucode_id) { goto error; } - /* http://credentiality2.blogspot.com/2015/09/beaglebone-pru-ddr-memory-access.html * get pointer to shared DDR @@ -208,7 +206,7 @@ int pru_c::start(enum prucode_enum prucode_id) { // verify PRU1 is executing its command loop mailbox->arm2pru_req = ARM2PRU_NOP; timeout.wait_ms(1); - if (mailbox->arm2pru_req != ARM2PRU_NONE) { + if (mailbox->arm2pru_req != ARM2PRU_NONE) { FATAL("PRU1 is not executing its command loop"); goto error; } diff --git a/10.01_base/2_src/arm/pru.hpp b/10.01_base/2_src/arm/pru.hpp index 70dc9bf..4ac5a94 100644 --- a/10.01_base/2_src/arm/pru.hpp +++ b/10.01_base/2_src/arm/pru.hpp @@ -2,37 +2,37 @@ Copyright (c) 2018, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - - Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 18-apr-2019 JH added PRU code dictionary - 12-nov-2018 JH entered beta phase + 18-apr-2019 JH added PRU code dictionary + 12-nov-2018 JH entered beta phase */ #ifndef _PRU_HPP_ @@ -43,8 +43,6 @@ #include "logsource.hpp" - - /*** PRU Shared addresses ***/ // Mailbox page & offset in PRU internal shared 12 KB RAM // Accessible by both PRUs, must be located in shared RAM @@ -57,35 +55,31 @@ #define PRU0_ENTRY_ADDR 0x00000000 #define PRU1_ENTRY_ADDR 0x00000000 - #ifndef PRU_MAILBOX_RAM_ID - #define PRU_MAILBOX_RAM_ID PRUSS0_SHARED_DATARAM - #define PRU_MAILBOX_RAM_OFFSET 0 +#define PRU_MAILBOX_RAM_ID PRUSS0_SHARED_DATARAM +#define PRU_MAILBOX_RAM_OFFSET 0 #endif // Device register page & offset in PRU0 8KB RAM mapped into PRU1 space // offset 0 == addr 0x2000 in linker cmd files for PRU1 projects. // For use with prussdrv_map_prumem() #ifndef PRU_DEVICEREGISTER_RAM_ID - #define PRU_DEVICEREGISTER_RAM_ID PRUSS0_PRU0_DATARAM - #define PRU_DEVICEREGISTER_RAM_OFFSET 0 +#define PRU_DEVICEREGISTER_RAM_ID PRUSS0_PRU0_DATARAM +#define PRU_DEVICEREGISTER_RAM_OFFSET 0 #endif - - - class pru_c: public logsource_c { public: // IDs for code variants, so callers can select one enum prucode_enum { - PRUCODE_EOD = 0, // special marker: end of dictionary - PRUCODE_NONE = 0, // no code running, RPU reset - PRUCODE_TEST = 1, // only selftest functions - PRUCODE_UNIBUS = 2 // regular UNIBUS operation + PRUCODE_EOD = 0, // special marker: end of dictionary + PRUCODE_NONE = 0, // no code running, RPU reset + PRUCODE_TEST = 1, // only selftest functions + PRUCODE_UNIBUS = 2 // regular UNIBUS operation // with or without physical CPU for arbitration - } ; + }; public: - enum prucode_enum prucode_id ; // currently running code + enum prucode_enum prucode_id; // currently running code pru_c(); int start(enum prucode_enum prucode_id); diff --git a/10.01_base/2_src/arm/storagecontroller.cpp b/10.01_base/2_src/arm/storagecontroller.cpp index 42255cc..8783952 100644 --- a/10.01_base/2_src/arm/storagecontroller.cpp +++ b/10.01_base/2_src/arm/storagecontroller.cpp @@ -48,7 +48,7 @@ bool storagecontroller_c::on_param_changed(parameter_c *param) { // 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) } diff --git a/10.01_base/2_src/arm/unibus.cpp b/10.01_base/2_src/arm/unibus.cpp index ea7c5e1..b246a1e 100644 --- a/10.01_base/2_src/arm/unibus.cpp +++ b/10.01_base/2_src/arm/unibus.cpp @@ -20,7 +20,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + jul-2019 JH rewrite: multiple parallel arbitration levels 12-nov-2018 JH entered beta phase */ @@ -40,6 +40,8 @@ #include "memoryimage.hpp" #include "mailbox.h" // for test of PRU code #include "utils.hpp" // for test of PRU code +#include "unibusadapter.hpp" // DMA, INTR + #include "unibus.h" /* Singleton */ @@ -47,8 +49,14 @@ unibus_c *unibus; unibus_c::unibus_c() { log_label = "UNIBUS"; - dma_bandwidth_percent = 50; - dma_wordcount = MAX_DMA_WORDCOUNT; + dma_request = new dma_request_c(NULL); + // priority backplane slot # for helper DMA not important, as typically used stand-alone + // (no other devioces on the backplane active, except perhaps "testcontroller") + dma_request->set_priority_slot(16); +} + +unibus_c::~unibus_c() { + delete dma_request; } /* return a 16 bit result, or TIMEOUT @@ -96,40 +104,31 @@ void unibus_c::init(void) { timeout.wait_ms(duration_ms); buslatches_setval(7, BIT(3), 0); */ - mailbox_execute(ARM2PRU_INITPULSE, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_INITPULSE); } /* Simulate a power cycle */ void unibus_c::powercycle(void) { - mailbox_execute(ARM2PRU_POWERCYCLE, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_POWERCYCLE); } -// do an UNIBUS INTR transaction with Arbitration by PDP-11 CPU -// todo: ARBITRATOR_MASTER? -void unibus_c::interrupt(uint8_t priority, uint16_t vector) { - assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); - switch (priority) { - case 4: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4; +void unibus_c::set_arbitration_mode(arbitration_mode_enum arbitration_mode) { + // switch pru to desired mode + switch (arbitration_mode) { + case unibus_c::ARBITRATION_MODE_NONE: + mailbox_execute(ARM2PRU_ARB_MODE_NONE); break; - case 5: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5; + case unibus_c::ARBITRATION_MODE_CLIENT: + mailbox_execute(ARM2PRU_ARB_MODE_CLIENT); break; - case 6: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6; - break; - case 7: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7; + case unibus_c::ARBITRATION_MODE_MASTER: + mailbox_execute(ARM2PRU_ARB_MODE_MASTER); break; default: - ERROR("unibus_interrupt(): Illegal priority %u, aborting", priority); - return; + printf("Illegal arbitration_mode %d\n", (int) arbitration_mode); + exit(1); } - mailbox->intr.vector = vector; - // mail last infinite, if processor priority above "priority" - // timeout ?? - mailbox_execute(ARM2PRU_INTR, ARM2PRU_NONE); } // do a DMA transaction with or without abritration (arbitration_client) @@ -137,36 +136,19 @@ void unibus_c::interrupt(uint8_t priority, uint16_t vector) { // if result = timeout: = // 0 = bus time, error address = mailbox->dma.cur_addr // 1 = all transfered -bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_t control, - uint32_t startaddr, unsigned blocksize) { +// A limit for time used by DMA can be compiled-in +bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, bool blocking, + uint8_t control, uint32_t startaddr, uint16_t *buffer, unsigned wordcount) { + int dma_bandwidth_percent = 50; // use 50% of time for DMA, rest for running PDP-11 CPU uint64_t dmatime_ns, totaltime_ns; - uint8_t dma_opcode = ARBITRATION_MODE_NONE; // inihibit compiler warnings // can access bus with DMA when there's a Bus Arbitrator assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); -// TODO: assert pru->prucode_features & (PRUCODE_FEATURE_DMA) ??? - // TODO: Arbitration Master waits for SACK, 11/34 blocks? - mailbox->dma.startaddr = startaddr; - mailbox->dma.control = control; - mailbox->dma.wordcount = blocksize; + set_arbitration_mode(arbitration_mode); // changes PRU behaviour + timeout.start(0); // no timeout, just running timer - - switch (arbitration_mode) { - case unibus_c::ARBITRATION_MODE_NONE: - dma_opcode = ARM2PRU_DMA_ARB_NONE; - break; - case unibus_c::ARBITRATION_MODE_CLIENT: - dma_opcode = ARM2PRU_DMA_ARB_CLIENT; - break; - case unibus_c::ARBITRATION_MODE_MASTER: - dma_opcode = ARM2PRU_DMA_ARB_MASTER; - break; - default: - FATAL("Illegal arbitration_mode"); - } - // wait until PRU ready - mailbox_execute(dma_opcode, ARM2PRU_NONE); + unibusadapter->DMA(*dma_request, blocking, control, startaddr, buffer, wordcount); dmatime_ns = timeout.elapsed_ns(); // wait before next transaction, to reduce Unibus bandwidth @@ -176,31 +158,26 @@ bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_ // 25% -> total = 4* dma totaltime_ns = (dmatime_ns * 100) / dma_bandwidth_percent; // whole transaction requires totaltime, dma already done -//INFO("DMA time= %lluus, waiting %lluus", dmatime_ns/1000, (totaltime_ns - dmatime_ns)/1000) ; timeout.wait_ns(totaltime_ns - dmatime_ns); - return (mailbox->dma.cur_status == DMA_STATE_READY); - // all other end states are errors, only error is bus timeout + return dma_request->success; // only useful if blocking } /* scan unibus addresses ascending from 0. * Stop on error, return first invalid address * return 0: no memory found at all * arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses + * words[]: buffer for whole UNIBUS address range, is filled with data */ uint32_t unibus_c::test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode) { // tests chunks of 128 word - bool timeout; unsigned addr = 0; - //SET_DEBUG_PIN0(0) ; - do { - // printf("unibus_test_sizer(): %06o..%06o\n", addr, addr+2*unibus_dma_wordcount-2) ; - timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, dma_wordcount); - addr += 2 * dma_wordcount; // prep for next round - } while (!timeout); - //SET_DEBUG_PIN0(1) ; // signal end - //SET_DEBUG_PIN0(0) ; - return mailbox->dma.cur_addr; // first non implemented address + + set_arbitration_mode(arbitration_mode); // changes PRU behaviour + // one big transaction, automatically split in chunks + unibusadapter->DMA(*dma_request, true, UNIBUS_CONTROL_DATI, addr, testwords, + UNIBUS_WORDCOUNT); + return dma_request->unibus_end_addr; // first non implemented address } /* @@ -213,32 +190,15 @@ uint32_t unibus_c::test_sizer(enum unibus_c::arbitration_mode_enum arbitration_m // // DMA blocksize can be choosen arbitrarily void unibus_c::mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words, - unsigned start_addr, unsigned end_addr, unsigned block_wordcount, - bool *timeout) { - unsigned block_start_addr = 0; - unsigned n; + unsigned unibus_start_addr, unsigned unibus_end_addr, bool *timeout) { + unsigned wordcount = (unibus_end_addr - unibus_start_addr) / 2 + 1; + uint16_t *buffer_start_addr = words + unibus_start_addr / 2; assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); - assert(block_wordcount <= MAX_DMA_WORDCOUNT); - *timeout = 0; - for (block_start_addr = start_addr; !*timeout && block_start_addr <= end_addr; - block_start_addr += 2 * block_wordcount) { - // trunc last block - n = (end_addr - block_start_addr) / 2 + 1; // words left until memend - if (n < block_wordcount) - block_wordcount = n; //trunc last buffer - // fill data into dma buffer - memcpy((void*) (mailbox->dma.words), (void*) (words + block_start_addr / 2), - 2 * block_wordcount); - /* for (n = 0; n < block_wordcount; n++) { - cur_addr = block_start_addr + 2 * n; - mailbox->dma.words[n] = words[cur_addr / 2]; - } - */*timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATO, block_start_addr, - block_wordcount); - if (*timeout) { - printf("\nWrite timeout @ 0%6o\n", mailbox->dma.cur_addr); - return; - } + *timeout = !dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, unibus_start_addr, + buffer_start_addr, wordcount); + if (*timeout) { + printf("\nWrite timeout @ 0%6o\n", mailbox->dma.cur_addr); + return; } } @@ -247,34 +207,54 @@ void unibus_c::mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, // DMA blocksize can be choosen arbitrarily // arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses void unibus_c::mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words, - uint32_t start_addr, uint32_t end_addr, unsigned block_wordcount, - bool *timeout) { - unsigned block_start_addr = 0; - unsigned n; + uint32_t unibus_start_addr, uint32_t unibus_end_addr, bool *timeout) { + unsigned wordcount = (unibus_end_addr - unibus_start_addr) / 2 + 1; + uint16_t *buffer_start_addr = words + unibus_start_addr / 2; assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); - assert(block_wordcount <= MAX_DMA_WORDCOUNT); - *timeout = 0; - for (block_start_addr = start_addr; !*timeout && block_start_addr <= end_addr; - block_start_addr += 2 * block_wordcount) { - // trunc last block - n = (end_addr - block_start_addr) / 2 + 1; // words left until memend - if (n < block_wordcount) - block_wordcount = n; //trunc last buffer - *timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATI, block_start_addr, - block_wordcount); + *timeout = !dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, unibus_start_addr, + buffer_start_addr, wordcount); + if (*timeout) { + printf("\nRead timeout @ 0%6o\n", mailbox->dma.cur_addr); + return; + } +} + +// read or write +void unibus_c::mem_access_random(enum unibus_c::arbitration_mode_enum arbitration_mode, + uint8_t unibus_control, uint16_t *words, uint32_t unibus_start_addr, + uint32_t unibus_end_addr, bool *timeout, uint32_t *block_counter) { + uint32_t block_unibus_start_addr, block_unibus_end_addr; + // in average, make 16 sub transactions + assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); + assert(unibus_control == UNIBUS_CONTROL_DATI || unibus_control == UNIBUS_CONTROL_DATO); + block_unibus_start_addr = unibus_start_addr; + // split transaction in random sized blocks + uint32_t max_block_wordcount = (unibus_end_addr - unibus_start_addr + 2) / 2; + + do { + uint16_t *block_buffer_start = words + block_unibus_start_addr / 2; + uint32_t block_wordcount; + do { + block_wordcount = random32_log(max_block_wordcount); + } while (block_wordcount < 1); + assert(block_wordcount < max_block_wordcount); + // wordcount limited by "words left to transfer" + block_wordcount = std::min(block_wordcount, + (unibus_end_addr - block_unibus_start_addr) / 2 + 1); + block_unibus_end_addr = block_unibus_start_addr + 2 * block_wordcount - 2; + assert(block_unibus_end_addr <= unibus_end_addr); + (*block_counter) += 1; + // printf("%06d: %5u words %06o-0%06o\n", *block_counter, block_wordcount, block_unibus_start_addr, block_unibus_end_addr) ; + *timeout = !dma(arbitration_mode, true, unibus_control, block_unibus_start_addr, + block_buffer_start, block_wordcount); if (*timeout) { - printf("\nRead timeout @ 0%6o\n", mailbox->dma.cur_addr); + printf("\n%s timeout @ 0%6o\n", control2text(unibus_control), + mailbox->dma.cur_addr); return; } - // move from DMA buffer into words[] - memcpy((void*) (words + block_start_addr / 2), (void*) (mailbox->dma.words), - 2 * block_wordcount); - /* for (n = 0; n < block_wordcount; n++) { - cur_addr = block_start_addr + 2 * n; - words[cur_addr / 2] = mailbox->dma.words[n]; - } - */} + block_unibus_start_addr = block_unibus_end_addr + 2; + } while (block_unibus_start_addr <= unibus_end_addr); } // arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses @@ -285,7 +265,7 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, bool timeout = 0, mismatch = 0; unsigned mismatch_count = 0; uint32_t cur_test_addr; - unsigned block_wordcount; + unsigned pass_count = 0, total_read_block_count = 0, total_write_block_count = 0; assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); @@ -298,25 +278,28 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, testwords[cur_test_addr / 2] = (cur_test_addr >> 1) & 0xffff; /**** 2. Write memory ****/ progress.put("W"); //info : full memory write - block_wordcount = 113; // something queer - mem_write(arbitration_mode, testwords, start_addr, end_addr, block_wordcount, &timeout); + mem_write(arbitration_mode, testwords, start_addr, end_addr, &timeout); /**** 3. read until ^C ****/ while (!SIGINTreceived && !timeout && !mismatch_count) { + pass_count++; + if (pass_count % 10 == 0) + progress.putf(" %d ", pass_count); + total_write_block_count++; // not randomized + total_read_block_count++; progress.put("R"); - block_wordcount = 67; // something queer // read back into unibus_membuffer[] - mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, - block_wordcount, &timeout); + mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, &timeout); // compare - SET_DEBUG_PIN0(0) ; for (mismatch_count = 0, cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2) { uint16_t cur_mem_val = membuffer->data.words[cur_test_addr / 2]; mismatch = (testwords[cur_test_addr / 2] != cur_mem_val); if (mismatch) { - SET_DEBUG_PIN0(1) ; // trigger - SET_DEBUG_PIN0(0) ; + ARM_DEBUG_PIN0(1) + ; // trigger + ARM_DEBUG_PIN0(0) + ; } if (mismatch && ++mismatch_count <= MAX_ERROR_COUNT) // print only first errors printf( @@ -329,32 +312,39 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, case 2: // full write, full read /**** 1. Full write generate test values */ +// start_addr = 0; +// end_addr = 076; while (!SIGINTreceived && !timeout && !mismatch_count) { + pass_count++; + if (pass_count % 10 == 0) + progress.putf(" %d ", pass_count); + for (cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2) - testwords[cur_test_addr / 2] = random24() & 0xffff; + testwords[cur_test_addr / 2] = random24() & 0xffff; // random +// testwords[cur_test_addr / 2] = (cur_test_addr >> 1) & 0xffff; // linear + progress.put("W"); //info : full memory write - block_wordcount = 97; // something queer - mem_write(arbitration_mode, testwords, start_addr, end_addr, block_wordcount, - &timeout); + mem_access_random(arbitration_mode, UNIBUS_CONTROL_DATO, testwords, start_addr, + end_addr, &timeout, &total_write_block_count); if (SIGINTreceived || timeout) break; // leave loop // first full read progress.put("R"); //info : full memory write - block_wordcount = 111; // something queer // read back into unibus_membuffer[] - mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, - block_wordcount, &timeout); + mem_access_random(arbitration_mode, UNIBUS_CONTROL_DATI, membuffer->data.words, + start_addr, end_addr, &timeout, &total_read_block_count); // compare - SET_DEBUG_PIN0(0) ; for (mismatch_count = 0, cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2) { uint16_t cur_mem_val = membuffer->data.words[cur_test_addr / 2]; mismatch = (testwords[cur_test_addr / 2] != cur_mem_val); if (mismatch) { - SET_DEBUG_PIN0(1) ; // trigger - SET_DEBUG_PIN0(0) ; + ARM_DEBUG_PIN0(1) + ; // trigger + ARM_DEBUG_PIN0(0) + ; } if (mismatch && ++mismatch_count <= MAX_ERROR_COUNT) // print only first errors printf( @@ -370,44 +360,7 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, printf("Stopped by error: %stimeout, %d mismatches\n", (timeout ? "" : "no "), mismatch_count); else - printf("All OK!\n"); + printf("All OK! Total %d passes, split into %d block writes and %d block reads\n", + pass_count, total_write_block_count, total_read_block_count); } -#ifdef USED -/* load a word buffer from file - * words assumed to be little endian (LSB first) - */ -void unibus_c::loadfromfile(char *fname, uint16_t *words, unsigned *bytecount) { - FILE *f; - unsigned n; - f = fopen(fname, "rb"); - if (!f) { - ERROR("unibus_loadfromfile(): could not open file \"%s\" for read.", fname); - return; - } - // try to read max address range, shorter files are OK - n = fread((void *) words, 1, 2 * UNIBUS_WORDCOUNT, f); - fclose(f); - *bytecount = n; - INFO("Read %d bytes from \"%s\".", *bytecount, fname); -} - -/* save a word buffer to file */ -void unibus_c::savetofile(char *fname, uint16_t *words, unsigned bytecount) { - FILE *f; - unsigned n; - f = fopen(fname, "w"); - if (!f) { - ERROR("unibus_savetofile(): could not open file \"%s\" for write.", fname); - return; - } - n = fwrite((void *) words, 1, bytecount, f); - if (n != bytecount) { - ERROR("unibus_savetofile(): tried to write %u bytes, only %d successful.", bytecount, - n); - } - fclose(f); - INFO("Wrote %d bytes to \"%s\".", n, fname); -} -#endif - diff --git a/10.01_base/2_src/arm/unibusadapter.cpp b/10.01_base/2_src/arm/unibusadapter.cpp index c6dd27b..7b46af2 100644 --- a/10.01_base/2_src/arm/unibusadapter.cpp +++ b/10.01_base/2_src/arm/unibusadapter.cpp @@ -1,6 +1,6 @@ /* unibusadapter.cpp: connects multiple "unibusdevices" to the PRU UNIBUS interface - Copyright (c) 2018, Joerg Hoppe + Copyright (c) 2018-2019, Joerg Hoppe j_hoppe@t-online.de, www.retrocmp.com Permission is hereby granted, free of charge, to any person obtaining a @@ -21,6 +21,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + jul-2019 JH rewrite: multiple parallel arbitration levels 12-nov-2018 JH entered beta phase unibusadapter @@ -65,40 +66,15 @@ using namespace std; #include "prussdrv.h" #include "pruss_intc_mapping.h" #include "iopageregister.h" +#include "priorityrequest.hpp" #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() { - -} - -irq_request_c::irq_request_c(unsigned level, unsigned vector) : - _level(level), _vector(vector), _isComplete(false) { - -} - -irq_request_c::~irq_request_c() { - -} - -void* bus_worker(void *context) { - unibusadapter_c* bus = reinterpret_cast(context); - bus->dma_worker(); - return nullptr; -} - unibusadapter_c *unibusadapter; // another Singleton // is registered in device_c.list ... order of static constructor calls ??? +bool unibusadapter_debug_flag = 0 ; + unibusadapter_c::unibusadapter_c() : - device_c(), _busWakeup_cond(PTHREAD_COND_INITIALIZER), _requestFinished_cond( - PTHREAD_COND_INITIALIZER), _busWorker_mutex(PTHREAD_MUTEX_INITIALIZER) { + device_c() { unsigned i; log_label = "UNAPT"; @@ -110,18 +86,9 @@ unibusadapter_c::unibusadapter_c() : line_INIT = false; line_DCLO = false; - // - // Start bus worker thread - // - pthread_attr_t attribs; - pthread_attr_init(&attribs); + requests_mutex = PTHREAD_MUTEX_INITIALIZER; - int status = pthread_create(&_busWorker_pthread, &attribs, &bus_worker, - reinterpret_cast(this)); - - if (status != 0) { - FATAL("Failed to start unibus worker thread. Status 0x%x", status); - } + requests_init(); } bool unibusadapter_c::on_param_changed(parameter_c *param) { @@ -136,214 +103,6 @@ void unibusadapter_c::on_power_changed(void) { void unibusadapter_c::on_init_changed(void) { } -// set state of INIT -void unibusadapter_c::worker_init_event() { - unsigned device_handle; - unibusdevice_c *device; - // notify device on changed of INIT line - DEBUG("worker_init_event(): INIT %s", line_INIT ? "asserted" : "deasserted"); - for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) - if ((device = devices[device_handle])) { - device->init_asserted = line_INIT; - device->on_init_changed(); - } - - // Clear bus request queues - rundown_bus_requests(); -} - -void unibusadapter_c::worker_power_event() { - unsigned device_handle; - unibusdevice_c *device; - // notify device on changed of INIT line - DEBUG("worker_power_event(): DCLO %s", line_DCLO ? "asserted" : "deasserted"); - for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) - if ((device = devices[device_handle])) { - device->power_down = line_DCLO; - device->on_power_changed(); - } - - // Clear bus request queues - rundown_bus_requests(); -} - -// process DATI/DATO access to active device registers - -void unibusadapter_c::worker_deviceregister_event() { - unsigned device_handle; - unibusdevice_c *device; - device_handle = mailbox->events.device_handle; - assert(device_handle); - device = devices[device_handle]; - unsigned evt_idx = mailbox->events.device_register_idx; - uint32_t evt_addr = mailbox->events.addr; - // normally evt_data == device_reg->shared_register->value - // but shared value gets desorted if INIT in same event clears the registers before DATO - uint16_t evt_data = mailbox->events.data; - unibusdevice_register_t *device_reg = &(device->registers[evt_idx]); - uint8_t unibus_control = mailbox->events.unibus_control; - - /* call device event callback - - The PRU has only one "value" for each register, but "active" registers have separate - read(DATI) and write(DATO) flipflops. So if register is "active: - BEFORE processing of logic state changes: - DATO: save written value into .active_write_val - restore changed shared PRU register with .active_read_val for next read - */ - if (device_reg->active_on_dati && !UNIBUS_CONTROL_ISDATO(unibus_control)) { - // register is read with DATI, this changes the logic state - evt_addr &= ~1; // make even - assert(evt_addr == device->base_addr.value + 2 * evt_idx); - unibus_control = UNIBUS_CONTROL_DATI; - // read access: dati-flipflops do not change - - // signal: changed by UNIBUS - device->log_register_event("DATI", device_reg); - - device->on_after_register_access(device_reg, unibus_control); - } else if (device_reg->active_on_dato && UNIBUS_CONTROL_ISDATO(unibus_control)) { -// uint16_t reg_value_written = device_reg->shared_register->value; - // restore value accessible by DATI - device_reg->shared_register->value = device_reg->active_dati_flipflops; - // Restauration of shared_register->value IS NOT ATOMIC against device logic threads. - // Devices must use only reg->active_dati_flipflops ! - switch (unibus_control) { - case UNIBUS_CONTROL_DATO: - // write into a register with separate read/write flipflops - assert(evt_addr == device->base_addr.value + 2 * evt_idx); - // clear unused bits, save written value - device_reg->active_dato_flipflops = evt_data & device_reg->writable_bits; - // signal: changed by UNIBUS - device->log_register_event("DATO", device_reg); - break; - case UNIBUS_CONTROL_DATOB: - // UNIBUS may access only 8bit half of register with DATOB. - // convert all active registers accesses to 16 bit - evt_data &= device_reg->writable_bits; // clear unused bits - // save written value - if (evt_addr & 1) // odd address: bits 15:8 written - device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0x00ff) - | (evt_data & 0xff00); - else - // even address : bits 7:0 written - device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0xff00) - | (evt_data & 0x00ff); - unibus_control = UNIBUS_CONTROL_DATO; // simulate 16 bit access - // signal: changed by UNIBUS - device->log_register_event("DATOB", device_reg); - break; - } - device->on_after_register_access(device_reg, unibus_control); - /* - DEBUG(LL_DEBUG, LC_UNIBUS, "dev.reg=%d.%d, %s, addr %06o, data %06o->%06o", - device_handle, evt_idx, - unibus_c::control2text(mailbox->event.unibus_control), evt_addr, - oldval, device_reg->shared_register->value); - */ - } -// only in worker()!! Starts PRU mailbox->events.eventmask &= ~EVENT_DEVICEREGISTER; // clear bit -} - -// runs in background, catches and distributes PRU events -void unibusadapter_c::worker(unsigned instance) { - UNUSED(instance) ; // only one - int res; - - // set thread priority to MAX. - // - fastest response to select() call in prussdrv_pru_wait_event_timeout() - // (minimal I/O latency) - // - not interrupted by other tasks while running - // check with tool "top" or "htop". - worker_init_realtime_priority(rt_max); // set to max prio - - while (!workers_terminate) { - // Timing: - // This is THE ONE mechanism where "realtime meets Linux" - // To respond to the PRU signal, Linux must wake up schedule this thread - // Test: - // - set RT prio of this worker thread to true 100% (SCHED_FIFO. max prio, - // /proc/sys/kernel/sched_rt_runtime_us = -1 - // - let the PRU pulse a GPIO on signal_set - // - let this worker raise a GPIO while its active - // - verify with scope that PRU - signal to worker start is about 100-300us - // MUST NOT GET LARGER on any Linux activity, - - // main loop - // wait 0.1 sec, just tell - /* The prussdrv_pru_wait_event() function returns the number of times - the event has taken place, as an unsigned int. There is no out-of- - band value to indicate error (and it can wrap around to 0 if you - run the program just a whole lot of times). */ - res = prussdrv_pru_wait_event_timeout(PRU_EVTOUT_0, 100000/*us*/); -//res = prussdrv_pru_wait_event(PRU_EVTOUT_0); - // PRU may have raised more than one event before signal is accepted. - // single combination of only INIT+DATI/O possible - prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); - // uses select() internally: 0 = timeout, -1 = error, else event count received - while (res > 0 && mailbox->events.eventmask) { // res is const -// SET_DEBUG_PIN0(1) ; // debug: PRU event accepted - // Process multiple events sent by PRU. - // - // while ARM accepts the signal, the PRU may set more events - // critical a mix of INIT and DATI/DATO: RESET and register access follow directly - // But no DATI/DATO occurs while INIT active. So reconstruct event order by - // processing order: INIT_DEASSERT, DATI/O, INIT_ASSERT, DCLO/ACLO - bool init_raising_edge = false; - bool init_falling_edge = false; - bool dclo_raising_edge = false; - bool dclo_falling_edge = false; - // DEBUG("mailbox->events: mask=0x%x", mailbox->events.eventmask); - if (mailbox->events.eventmask & EVENT_INITIALIZATIONSIGNALS) { - // robust: any change in ACLO/DCL=INIT updates state of all 3. - // Initial DCLO-cycle to PDP_11 intialize these states - if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_INIT) { - if (!line_INIT) - init_raising_edge = true; - line_INIT = true; - } else { - if (line_INIT) - init_falling_edge = true; - line_INIT = false; - } - if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_DCLO) { - if (!line_DCLO) - dclo_raising_edge = true; - line_DCLO = true; - } else { - if (line_DCLO) - dclo_falling_edge = true; - line_DCLO = false; - } - DEBUG( - "EVENT_INITIALIZATIONSIGNALS: (sigprev=0x%x,) cur=0x%x, init_rais=%d, init_fall=%d, dclo_rais=%d, dclo_fall=%d", - mailbox->events.initialization_signals_prev, - mailbox->events.initialization_signals_cur, init_raising_edge, - init_falling_edge, dclo_raising_edge, dclo_falling_edge); - - mailbox->events.eventmask &= ~EVENT_INITIALIZATIONSIGNALS; // ACK, now PRU continues - - if (dclo_raising_edge || dclo_falling_edge) - worker_power_event(); // power signal power change - } - if (init_falling_edge) // INIT asserted -> deasserted. DATI/DATO cycle only possible after that. - worker_init_event(); - if (mailbox->events.eventmask & EVENT_DEVICEREGISTER) { - // DATI/DATO - // DEBUG("EVENT_DEVICEREGISTER: control=%d, addr=%06o", (int)mailbox->events.unibus_control, mailbox->events.addr); - worker_deviceregister_event(); - mailbox->events.eventmask &= ~EVENT_DEVICEREGISTER; // ACK, now PRU continues - } - if (init_raising_edge) // INIT deasserted -> asserted DATI/DATO cycle only possible before that. - worker_init_event(); - - } -//SET_DEBUG_PIN0(0) ; // debug: PRU event processed - - // Signal to PRU: continue UNIBUS cycles now with SSYN deassert - } -} - // register_device ... "plug" the device into UNIBUs backplane // - assign handle // - setup register map for a device @@ -478,278 +237,781 @@ void unibusadapter_c::unregister_device(unibusdevice_c& device) { } } -/* interface for devices to issue DMA and INTR +/*** Access requests in [level,slot] table ***/ - **** !!! TODO: SORTING and SERIALIZING of requests required !!!! *** +// initialize slot tables in empty state +void unibusadapter_c::requests_init(void) { + for (unsigned level_index = 0; level_index < PRIORITY_LEVEL_COUNT; level_index++) { + priority_request_level_c *prl = &request_levels[level_index]; + for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++) + prl->slot_request[slot] = NULL; + prl->slot_request_mask = 0; + prl->active = NULL; + } +} - device has "slot" nr after registration +// put a request into the level/slot table +// do not yet active! +void unibusadapter_c::request_schedule(priority_request_c& request) { + // Must run under pthread_mutex_lock(&requests_mutex); + priority_request_level_c *prl = &request_levels[request.level_index]; + // DEBUG("request_schedule") ; + + // a device may reraise on of its own interrupts, but not an DMA on same slot + if (dynamic_cast(&request)) { + if (prl->slot_request[request.slot] != NULL) + FATAL("Concurrent DMA requested for slot %d.", (unsigned )request.slot); + } else if (dynamic_cast(&request)) { + if (prl->slot_request[request.slot] != NULL) { + unibusdevice_c *slotdevice = prl->slot_request[request.slot]->device; + if (slotdevice != request.device) + FATAL( + "Devices %s and %s share both slot %u for INTR request with priority index %u", + slotdevice ? slotdevice->name.value.c_str() : "NULL", + request.device->name.value.c_str(), (unsigned )request.slot, + (unsigned )request.level_index); + // DEBUG("request_schedule(): update request %p into level %u, slot %u",&request, request.level_index, request.slot); + } else { + // DEBUG("request_schedule(): insert request %p into level %u, slot %u",&request, request.level_index, request.slot); + } + } + + prl->slot_request[request.slot] = &request; // mark slot with request + prl->slot_request_mask |= (1 << request.slot); // set slot bit +} + +// Cancel all pending device_DMA and IRQ requests of every level. +// requests which are active on the PRU (->active) are left running, +// and the PRU terminates DMA sequences on INIT. +void unibusadapter_c::requests_cancel_scheduled(void) { + priority_request_c *req; + + // Must run under pthread_mutex_lock(&requests_mutex); + for (unsigned level_index = 0; level_index < PRIORITY_LEVEL_COUNT; level_index++) { + priority_request_level_c *prl = &request_levels[level_index]; + prl->slot_request_mask = 0; // clear alls slot from request + prl->active = NULL ; + + for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++) + if ((req = prl->slot_request[slot])) { + dma_request_c *dmareq; + req->executing_on_PRU = false; + req->complete = true; + if ((dmareq = dynamic_cast(req))) + dmareq->success = false; // device gets an DMA error, but will not understand + prl->slot_request[slot] = NULL; + // signal to blockin DMA() or INTR() + pthread_mutex_unlock(&req->complete_mutex); + //pthread_cond_signal(&req->complete_cond); + } + } +} + +/* + // is a request of given level active on the PRU? + bool unibusadapter_c::request_is_active(unsigned level_index) { + // Must run under pthread_mutex_lock(&requests_mutex); + priority_request_level_c *prl = &request_levels[level_index]; + return (prl->active != NULL); + } */ -// internal only: is any DMA or INTR issued by UNIBONE active? -// false: UNIBUS DMA or INTR pending or in progress -// true: new DMA or INTR may be started -bool unibusadapter_c::request_DMA_active(const char *error_info) { - if (mailbox->arm2pru_req == ARM2PRU_DMA_ARB_NONE - || mailbox->arm2pru_req == ARM2PRU_DMA_ARB_CLIENT - || mailbox->arm2pru_req == ARM2PRU_DMA_ARB_MASTER) { - if (error_info) - ERROR("%s: DMA requests active!", error_info); - return true; +// find highest prioritized request for a given level, via slots +priority_request_c *unibusadapter_c::request_activate_lowest_slot(unsigned level_index) { + /* To find the lowest slot with an active request of priority 'level' + gcc __builtin_ffs "Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero. " + (= slow with highest priority within 'level') + Is implemented on ARM as just 2 opcodes: rbit (bit reverse), clz (count number of leading zeros) + VERY FAST (without sorting list) + */ + // Must run under pthread_mutex_lock(&requests_mutex); + priority_request_level_c *prl = &request_levels[level_index]; + priority_request_c *rq; + + assert(prl->active == NULL); + // DEBUG("request_activate_lowest_slot") ; + + unsigned slot = __builtin_ffs(prl->slot_request_mask); + if (slot == 0) + rq = NULL; // no slot requesting on this level + else { + rq = prl->slot_request[slot - 1]; // slot 0 -> bit 0 -> slot=1 + assert(rq != NULL); + } + prl->active = rq; + // if (prl->active) + // DEBUG("request_activate_lowest_slot(): ->active = dma_request %p, level %u, slot %u",prl->active, prl->active->level_index, prl->active->slot); + // else + // DEBUG("request_activate_lowest_slot(): ->active = NULL"); + assert((prl->slot_request_mask == 0) == (prl->active == NULL)) ; + + + return rq; +} + +// is any request of higher or same level executed? Is the next request executed delayed? +bool unibusadapter_c::request_is_blocking_active(uint8_t level_index) { + while (level_index < PRIORITY_LEVEL_COUNT) { + priority_request_level_c *prl = &request_levels[level_index]; + if (prl->active) + return true; + if (prl->slot_request_mask) + return true; + level_index++ ; } -// TODO: check queue for another device request -// rely on RL11 to check for completion and sorting DMA/INTR requests. return false; } -// if error_info: create error if INTR running -bool unibusadapter_c::request_INTR_active(const char *error_info) { - if (mailbox->arm2pru_req == ARM2PRU_INTR) { - if (error_info) - ERROR("%s: INTR requests active!", error_info); - return true; +// helper: push the active request to the PRU for execution +// VB: the next request to schedule already calculated and saved in priority_request_level_c.active +void unibusadapter_c::request_execute_active_on_PRU(unsigned level_index) { + priority_request_level_c *prl = &request_levels[level_index]; + assert(prl->active); + // Must run under pthread_mutex_lock(&requests_mutex); + // DEBUG("request_execute_active_on_PRU(level_idx=%u)", level_index); + if (level_index == PRIORITY_LEVEL_INDEX_NPR) { + dma_request_c *dmareq = dynamic_cast(prl->active); + assert(dmareq); + + // We do the device_DMA transfer in chunks so we can handle arbitrary buffer sizes. + // (the PRU mailbox has limited space available.) + + // Push the chunk to the PRU. + unsigned wordcount_remaining = dmareq->wordcount - dmareq->wordcount_completed_chunks(); + //dmareq->chunk_max_words = 2; // TEST + dmareq->chunk_words = std::min(dmareq->chunk_max_words, wordcount_remaining); + + assert(dmareq->chunk_words); // if complete, the dmareq should not be active anymore + + mailbox->dma.startaddr = dmareq->chunk_unibus_start_addr; + mailbox->dma.control = dmareq->unibus_control; + mailbox->dma.wordcount = dmareq->chunk_words; + + // Copy outgoing data into mailbox device_DMA buffer + if (dmareq->unibus_control == UNIBUS_CONTROL_DATO) { + memcpy((void*) mailbox->dma.words, dmareq->chunk_buffer_start(), + 2 * dmareq->chunk_words); + } + + // + // Start the PRU: + // signal still not cleared in worker() while processing this + // assert(mailbox->events.event_dma == 0); // previous signal must have been processed + // DEBUG("request_execute_active_on_PRU() DMA: ->active = dma_request %p, start = 0%06o, control=%u, wordcount=%u, data=0%06o ...", dmareq, + // mailbox->dma.startaddr, (unsigned)mailbox->dma.control, (unsigned)mailbox->dma.wordcount, (unsigned)mailbox->dma.words[0]); + + mailbox_execute(ARM2PRU_DMA); + // scheduling is fast, on complete there's a signal. + dmareq->executing_on_PRU = true; + + /* if DMA is done in multiple chunks, + then after PRU is complete, we don not call "active_complete() to remove the request. + Instead we leave it active, with transferrred data clipped from buffer start. + the "complete" signal will relaunch the remaining dma request automatically + we need a new address "chunk_start_addr" in dma_request_c + + As side effect, a higher priorized device may be serviced before the next chunk is transmitted, + This is intended and prevents data loss. + */ + + } else { + // Not DMA? must be INTR + intr_request_c *intrreq = dynamic_cast(prl->active); + assert(intrreq); + // Handle interrupt request to PRU. Setup mailbox: + mailbox->intr.level_index = intrreq->level_index; + mailbox->intr.vector[intrreq->level_index] = intrreq->vector; + if (intrreq->interrupt_register) + mailbox->intr.iopage_register_handle = + intrreq->interrupt_register->shared_register_handle; + else + mailbox->intr.iopage_register_handle = 0; // none + mailbox->intr.iopage_register_value = intrreq->interrupt_register_value; + // decode index 0..3 = BR4..BR7 => PRU signal register bit + switch (intrreq->level_index) { + case PRIORITY_LEVEL_INDEX_BR4: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B4; + break; + case PRIORITY_LEVEL_INDEX_BR5: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B5; + break; + case PRIORITY_LEVEL_INDEX_BR6: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B6; + break; + case PRIORITY_LEVEL_INDEX_BR7: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B7; + break; + + default: + ERROR("Request_INTR(): Illegal priority %u, aborting", intrreq->level_index); + return; + } + + // start on PRU + // PRU have got arbitration for an INTR of different level in the mean time: + // assert(mailbox->events.event_intr == 0) would trigger + mailbox_execute(ARM2PRU_INTR); + intrreq->executing_on_PRU = true; // waiting for GRANT + // PRU now changes state } -// TODO: check queue for another device request -// rely on RL11 to check for completion and sorting DMA/INTR requests. - return false; + + /* when PRU is finished, the worker() gets a signal, + then worker_dma_chunk_complete_event() or worker_intr_complete_event() is called. + On INTR or last DMA chunk, the request is completed. + It is removed from the slot schedule table and request->compelte_conmd is signaled + to DMA() or INTR() + */ +} + +// remove request pointer currently handled by PRU from tables +// also called on INTR_CANCEL +void unibusadapter_c::request_active_complete(unsigned level_index) { + // Must run under pthread_mutex_lock(&requests_mutex); + + priority_request_level_c *prl = &request_levels[level_index]; + if (!prl->active) // PRU compelted after INIT cleared the tables + return; + // DEBUG("request_active_complete") ; + + unsigned slot = prl->active->slot; + //if (prl->slot_request[slot] != prl->active) + // mailbox_execute(ARM2PRU_HALT) ; // LA: trigger on timeout REG_WRITE + // active not in table, if table cleared by INIT requests_cancel_scheduled() + assert(prl->slot_request[slot] == prl->active); // must still be in table + + // mark as complete + prl->active->executing_on_PRU = false; +// prl->active->complete = true; + // remove table entries + prl->slot_request[slot] = NULL; // clear slot from request + prl->slot_request_mask &= ~(1 << slot); // mask out slot bit + + priority_request_c *tmprq = prl->active; + prl->active = NULL; + + // signal to DMA() or INTR() + tmprq->complete = true; // close to signal + pthread_mutex_unlock(&tmprq->complete_mutex); + +// pthread_cond_signal(&tmprq->complete_cond); + +// pthread_cond_broadcast(&tmprq->complete_cond); } // Request a DMA cycle from Arbitrator. // 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) { +// Blocking == true: DMA() wait for request to complete +// Blocking == false: return immediately, the device logic should +// evaluate the request.complete flag or wait for the mutex - // - // 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); +void unibusadapter_c::DMA(dma_request_c& dma_request, bool blocking, uint8_t unibus_control, + uint32_t unibus_addr, uint16_t *buffer, uint32_t wordcount) { + assert(dma_request.slot < PRIORITY_SLOT_COUNT); + assert(dma_request.level_index == PRIORITY_LEVEL_INDEX_NPR); - pthread_mutex_lock(&_busWorker_mutex); - _dmaRequests.push(&request); - pthread_cond_signal(&_busWakeup_cond); - pthread_mutex_unlock(&_busWorker_mutex); + // setup device request + assert(wordcount > 0); + assert((unibus_addr + 2*wordcount) <= 2*UNIBUS_WORDCOUNT); - DEBUG("DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, - wordcount); + pthread_mutex_lock(&requests_mutex); // lock schedule table operations - // - // Wait for request to finish. - // - pthread_mutex_lock(&_busWorker_mutex); - while (!request.IsComplete()) { - pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex); + // In contrast to re-raised INTR, overlapping DMA requests from same board + // must not be ignored (different DATA situation) and are an device implementation error. + // If a device indeed has multiple DMA channels, it must use different pseudo-slots. + priority_request_level_c *prl = &request_levels[PRIORITY_LEVEL_INDEX_NPR]; + assert(prl->slot_request[dma_request.slot] == NULL); // not scheduled or prev completed + + pthread_mutex_lock(&dma_request.complete_mutex); // lock early, else PRU can signal cond before we lock + // dma_request.level-index, priority_slot in constructor + dma_request.complete = false; + dma_request.executing_on_PRU = false; + dma_request.unibus_control = unibus_control; + dma_request.unibus_start_addr = unibus_addr; + dma_request.chunk_unibus_start_addr = unibus_addr; + dma_request.unibus_end_addr = 0; // last transfered addr, or error position + dma_request.buffer = buffer; + dma_request.wordcount = wordcount; + dma_request.chunk_max_words = PRU_MAX_DMA_WORDCOUNT; // PRU limit, maybe less + // DEBUG("DMA(): initialized dma_request %p",&dma_request); + + // put into schedule tables + + request_schedule(dma_request); // assertion, if twice for same slot + if (!prl->active) { +// if (!request_is_active(dma_request.level_index)) { + // no device_DMA current performed: start immediately + // else triggered by PRU signals + request_activate_lowest_slot(dma_request.level_index); + request_execute_active_on_PRU(dma_request.level_index); } - pthread_mutex_unlock(&_busWorker_mutex); + pthread_mutex_unlock(&requests_mutex); - if (unibus_end_addr) - *unibus_end_addr = request.GetUnibusEndAddr(); + // DEBUG("device DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, wordcount); + if (blocking) { + // acquire locked mutex => wait for worker to release + pthread_mutex_lock(&dma_request.complete_mutex); + pthread_mutex_unlock(&dma_request.complete_mutex); - return request.GetSuccess(); + /* + // DMA() is blocking: Wait for request to finish. + // pthread_mutex_lock(&dma_request.mutex); + while (!dma_request.complete) { + // busy waiting OK + int res = pthread_cond_wait(&dma_request.complete_cond, &dma_request.complete_mutex); + assert(!res) ; + dma_request.dbg_complete_sig_received++ ; + } + */ + } } -void unibusadapter_c::dma_worker() { - //worker_init_realtime_priority(rt_device); - while (true) { - dma_request_c* dmaReq = nullptr; - irq_request_c* irqReq = nullptr; +// A device raises an interrupt and simultaneously changes a value in +// one of its registers, the "interrupt register". +// ''interrupt_register' may be NULL if none. +// INTR() is NOT BLOCKING: it returns immediately. +// the actual interrupt vector is transfered when CPU interrupt level is lowered enough and +// other arbitration rules apply, which may never be the case. +// While pending, device may call INTR() again, causing waiting PRU requests to be modified. - // - // Wait for the next request. - // - pthread_mutex_lock(&_busWorker_mutex); - while (_dmaRequests.empty() && _irqRequests.empty()) { - pthread_cond_wait(&_busWakeup_cond, &_busWorker_mutex); +void unibusadapter_c::INTR(intr_request_c& intr_request, + unibusdevice_register_t *interrupt_register, uint16_t interrupt_register_value) { + assert(intr_request.slot < PRIORITY_SLOT_COUNT); + assert(intr_request.level_index <= 3); + assert((intr_request.vector & 3) == 0); // multiple of 2 words + + priority_request_level_c *prl = &request_levels[intr_request.level_index]; + + pthread_mutex_lock(&requests_mutex); // lock schedule table operations + + // Is an INTR with same slot and level already executed on PRU + // or waiting in the schedule table? + // If yes: do not re-raise, will be completed at some time later. + if (prl->slot_request[intr_request.slot] != NULL) { + intr_request_c *scheduled_intr_req = + dynamic_cast(prl->slot_request[intr_request.slot]); + assert(scheduled_intr_req); + // A device may re-raised a pending INTR again + // (quite normal situation when other ISRs block, CPU overload) + // A re-raise will be ignored. + // ! Another device MAY NOT reraise an INTR with same slot/level + // ! (else complete signals may be routed to wrong device) + assert(scheduled_intr_req->device == intr_request.device); + assert(scheduled_intr_req->vector == intr_request.vector); + // if different vector, it may not be ignored -> change in program flow + + // If device uses multiple INTRs with different vectors (DL11 rcv+xmt), + // it must use different pseudo-slots. + + // scheduled and request_active_complete() not called + pthread_mutex_unlock(&requests_mutex); + return; // do not schedule a 2nd time + } + + intr_request.complete = false; + intr_request.executing_on_PRU = false; + // intr_request.level_index, priority_slot, vector in constructor + intr_request.interrupt_register = interrupt_register; + intr_request.interrupt_register_value = interrupt_register_value; + if (interrupt_register) + assert(intr_request.device == interrupt_register->device); + + // The associated device interrupt register (if any) should be updated + // atomically with raising the INTR signal line by PRU. +//if (unibusadapter_debug_flag) +// printf("break here") ; + if (request_is_blocking_active(intr_request.level_index) && intr_request.interrupt_register) { + // one or more another requests are handled by PRU: INTR signal delayed by Arbitrator, + // write intr register asynchronically here. + intr_request.device->set_register_dati_value(intr_request.interrupt_register, + interrupt_register_value, __func__); + intr_request.interrupt_register = NULL; // don't do a 2nd time } - // - // We have a request: prioritize IRQ over DMA, dequeue from the requisite - // queue and get to work. - // - if (!_irqRequests.empty()) { - irqReq = _irqRequests.front(); - _irqRequests.pop(); - } else { - dmaReq = _dmaRequests.front(); - _dmaRequests.pop(); + // put into schedule tables + request_schedule(intr_request); // assertion, if twice for same slot + + if (!prl->active) { + // INTR of this level can be raised immediately + // If other level active, let PRU atomically set the interrupt register value. + request_activate_lowest_slot(intr_request.level_index); + request_execute_active_on_PRU(intr_request.level_index); + // else activation triggered by PRU signal in worker() + } + + pthread_mutex_unlock(&requests_mutex); // work on schedule table finished + + /* + // If INTR() is blocking: Wait for request to finish. + pthread_mutex_lock(&intr_request.mutex); + while (!intr_request.complete) { + pthread_cond_wait(&intr_request.complete_cond, &intr_request.mutex); + } + pthread_mutex_unlock(&intr_request.mutex); + */ +} + +/* A device may cancel an INTR request, if not yet GRANTed by Arbitrator. + Maybe useful if device sees INTR is not handeled by Arbitrator due to + blocked interrupt of this level. + Relevance of this usage pattern unclear, but used in KW11 diag, ZDLDI0 Test 17. + After cancelation, ARM receives NO completion event from PRU. + */ +void unibusadapter_c::cancel_INTR(intr_request_c& intr_request) { + uint8_t level_index = intr_request.level_index; // alias + priority_request_level_c *prl = &request_levels[level_index]; + if (prl->slot_request[intr_request.slot] == NULL) + return ; // not scheduled or active + + pthread_mutex_lock(&requests_mutex); // lock schedule table operations + if (&intr_request == prl->active) { + // already on PRU + mailbox_execute(ARM2PRU_INTR_CANCEL); + request_active_complete(level_index); + + // restart next request + request_activate_lowest_slot(level_index); + if (prl->active) + request_execute_active_on_PRU(level_index); + } else { + // not active on PRU: just remove from schedule table + prl->slot_request[intr_request.slot] = NULL; // clear slot from request + prl->slot_request_mask &= ~(1 << intr_request.slot); // mask out slot bit + } + // both empty, or both filled + assert((prl->slot_request_mask == 0) == (prl->active == NULL)) ; + pthread_mutex_unlock(&intr_request.complete_mutex); + + pthread_mutex_unlock(&requests_mutex); // lock schedule table operations + +} + +// set state of INIT +void unibusadapter_c::worker_init_event() { + unsigned device_handle; + unibusdevice_c *device; + // notify device on change of INIT line + DEBUG("worker_init_event(): INIT %s", line_INIT ? "asserted" : "deasserted"); + for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) + if ((device = devices[device_handle])) { + device->init_asserted = line_INIT; + device->on_init_changed(); } - 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)); + // Clear bus request queues + pthread_mutex_lock(&requests_mutex); + requests_cancel_scheduled(); + pthread_mutex_unlock(&requests_mutex); +} - 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. +void unibusadapter_c::worker_power_event() { + unsigned device_handle; + unibusdevice_c *device; + // notify device on change of DC_LO line + DEBUG("worker_power_event(): DCLO %s", line_DCLO ? "asserted" : "deasserted"); + for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) + if ((device = devices[device_handle])) { + device->power_down = line_DCLO; + device->on_power_changed(); + } - uint32_t maxTransferSize = 512; + // Clear bus request queues + pthread_mutex_lock(&requests_mutex); + requests_cancel_scheduled(); + pthread_mutex_unlock(&requests_mutex); +} - uint32_t wordCount = dmaReq->GetWordCount(); - uint32_t unibusAddr = dmaReq->GetUnibusStartAddr(); - uint32_t bufferOffset = 0; +// process DATI/DATO access to active device registers - while (wordCount > 0) { - uint32_t chunkSize = std::min(maxTransferSize, wordCount); +void unibusadapter_c::worker_deviceregister_event() { + unsigned device_handle; + unibusdevice_c *device; + device_handle = mailbox->events.device_handle; + assert(device_handle); + device = devices[device_handle]; + unsigned evt_idx = mailbox->events.device_register_idx; + uint32_t evt_addr = mailbox->events.addr; + // normally evt_data == device_reg->shared_register->value + // but shared value gets desorted if INIT in same event clears the registers before DATO + uint16_t evt_data = mailbox->events.data; + unibusdevice_register_t *device_reg = &(device->registers[evt_idx]); + uint8_t unibus_control = mailbox->events.unibus_control; - mailbox->dma.startaddr = unibusAddr + bufferOffset * 2; - mailbox->dma.control = dmaReq->GetUnibusControl(); - mailbox->dma.wordcount = chunkSize; + /* call device event callback - // Copy outgoing data into maibox DMA buffer - if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO) { - memcpy((void*) mailbox->dma.words, dmaReq->GetBuffer() + bufferOffset, - 2 * chunkSize); - } + The PRU has only one "value" for each register, but "active" registers have separate + read(DATI) and write(DATO) flipflops. So if register is "active: + BEFORE processing of logic state changes: + DATO: save written value into .active_write_val + restore changed shared PRU register with .active_read_val for next read + */ + if (device_reg->active_on_dati && !UNIBUS_CONTROL_ISDATO(unibus_control)) { + // register is read with DATI, this changes the logic state + evt_addr &= ~1; // make even + assert(evt_addr == device->base_addr.value + 2 * evt_idx); + unibus_control = UNIBUS_CONTROL_DATI; + // read access: dati-flipflops do not change - // - // Start the PRU: - mailbox->arm2pru_req = ARM2PRU_DMA_ARB_CLIENT; + // signal: changed by UNIBUS + device->log_register_event("DATI", device_reg); - // - // 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) { - timeout.wait_us(50); - retries++; - } + device->on_after_register_access(device_reg, unibus_control); + } else if (device_reg->active_on_dato && UNIBUS_CONTROL_ISDATO(unibus_control)) { + // uint16_t reg_value_written = device_reg->shared_register->value; + // restore value accessible by DATI + device_reg->shared_register->value = device_reg->active_dati_flipflops; + // Restauration of shared_register->value IS NOT ATOMIC against device logic threads. + // Devices must use only reg->active_dati_flipflops ! + switch (unibus_control) { + case UNIBUS_CONTROL_DATO: + // write into a register with separate read/write flipflops + assert(evt_addr == device->base_addr.value + 2 * evt_idx); + // clear unused bits, save written value + device_reg->active_dato_flipflops = evt_data & device_reg->writable_bits; + // signal: changed by UNIBUS + device->log_register_event("DATO", device_reg); + break; + case UNIBUS_CONTROL_DATOB: + // UNIBUS may access only 8bit half of register with DATOB. + // convert all active registers accesses to 16 bit + evt_data &= device_reg->writable_bits; // clear unused bits + // save written value + if (evt_addr & 1) // odd address: bits 15:8 written + device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0x00ff) + | (evt_data & 0xff00); + else + // even address : bits 7:0 written + device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0xff00) + | (evt_data & 0x00ff); + unibus_control = UNIBUS_CONTROL_DATO; // simulate 16 bit access + // signal: changed by UNIBUS + device->log_register_event("DATOB", device_reg); + break; + } + device->on_after_register_access(device_reg, unibus_control); + /* + DEBUG(LL_DEBUG, LC_UNIBUS, "dev.reg=%d.%d, %s, addr %06o, data %06o->%06o", + device_handle, evt_idx, + unibus_c::control2text(mailbox->event.unibus_control), evt_addr, + oldval, device_reg->shared_register->value); + */ + } +} - // - // TODO: this should not be necessary. There are rare occasions - // where it appears that the PRU dma transfer is interrupted - // but never clears the DMA active flag, so we hang in the loop above - // forever. - // Nothing to do in that case but give up. - // And log the issue. Should get to the root of this.. - // - if (retries == 10000) { - ERROR("dma timeout"); - } +// called by PRU signal when DMA transmission complete +void unibusadapter_c::worker_dma_chunk_complete_event() { + priority_request_level_c *prl = &request_levels[PRIORITY_LEVEL_INDEX_NPR]; + bool more_chunks; + // Must run under pthread_mutex_lock(&requests_mutex) ; - 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); - } - wordCount -= chunkSize; - bufferOffset += chunkSize; - } + dma_request_c *dmareq = dynamic_cast(prl->active); - dmaReq->SetUnibusEndAddr(mailbox->dma.cur_addr); - dmaReq->SetSuccess(mailbox->dma.cur_status == DMA_STATE_READY); - // no success: UnibusEndAddr is first failed address + assert(dmareq != NULL); + dmareq->unibus_end_addr = mailbox->dma.cur_addr; // track emnd of trasnmission, eror position + unsigned wordcount_transferred = dmareq->wordcount_completed_chunks() + + mailbox->dma.wordcount; + assert(wordcount_transferred <= dmareq->wordcount); + if (mailbox->dma.control == UNIBUS_CONTROL_DATI) { + // guard against buffer overrun + // PRU read chunk data from UNIBUS into mailbox + // copy result cur_DMA_wordcount from mailbox->DMA buffer to cur_DMA_buffer + memcpy(dmareq->chunk_buffer_start(), (void *) mailbox->dma.words, + 2 * mailbox->dma.wordcount); + } + if (mailbox->dma.cur_status != DMA_STATE_READY) { + // failure: abort remaining chunks + dmareq->success = false; + more_chunks = false; + } else if (wordcount_transferred == dmareq->wordcount) { + // last chunk completed + dmareq->success = true; + more_chunks = false; + } else { + // more data to transfer: next chunk. + dmareq->chunk_unibus_start_addr = mailbox->dma.cur_addr + 2; + // dmarequest remains prl->active and ->busy - assert( - dmaReq->GetUnibusStartAddr() + dmaReq->GetWordCount() * 2 - == mailbox->dma.cur_addr + 2); + // re-activate this request, or choose another with higher slot priority, + // inserted in parallel (interrupt this DMA) + prl->active = NULL; + request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR); + request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + more_chunks = true; + // DEBUG("DMA chunk: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", + // unibus->control2text(mailbox->dma.control), mailbox->dma.startaddr, + // mailbox->dma.cur_addr, mailbox->dma.wordcount, mailbox->dma.words[0], + // mailbox->dma.words[1], dmareq->success ? "OK" : "TIMEOUT"); + + } + if (!more_chunks) { + DEBUG("DMA ready: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", + unibus->control2text(dmareq->unibus_control), dmareq->unibus_start_addr, + dmareq->unibus_end_addr, dmareq->wordcount, dmareq->buffer[0], + dmareq->buffer[1], dmareq->success ? "OK" : "TIMEOUT"); + + // clear from schedule table of this level + request_active_complete(PRIORITY_LEVEL_INDEX_NPR); + + // check and execute DMA on other priority_slot + if (request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR)) + request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + } +} + +// called by PRU signal when INTR vector transmission complete +// OR the request has been canceled +// priority_level_index: 0..3 = BR4..BR7 +void unibusadapter_c::worker_intr_complete_event(uint8_t level_index) { + // Must run under pthread_mutex_lock(&requests_mutex) ; + priority_request_level_c *prl = &request_levels[level_index]; + + // if 1.st opcode of an ISR is a clear of INTR condition, + // cancle_INTR() may be called in worker_deviceregsiter_event() + // then the request is aloready remoced ferom schedule table. + //assert(prl->active); + + // clear from schedule table of this level + request_active_complete(level_index); + + // activate next request of this level on PRU for priority arbitration + request_activate_lowest_slot(level_index); + if (prl->active) + request_execute_active_on_PRU(level_index); + // else INTRs for all slots of this level completed +} + +// runs in background, catches and distributes PRU events +void unibusadapter_c::worker(unsigned instance) { + UNUSED(instance); // only one + int res; + bool any_event; + + // set thread priority to MAX. + // - fastest response to select() call in prussdrv_pru_wait_event_timeout() + // (minimal I/O latency) + // - not interrupted by other tasks while running + // check with tool "top" or "htop". + worker_init_realtime_priority(rt_device); // do not block while debugging + // worker_init_realtime_priority(rt_max); // set to max prio + + // mailbox may be un-initialized + + while (!workers_terminate) { + // Timing: + // This is THE ONE mechanism where "realtime meets Linux" + // To respond to the PRU signal, Linux must wake up schedule this thread + // Test: + // - set RT prio of this worker thread to true 100% (SCHED_FIFO. max prio, + // /proc/sys/kernel/sched_rt_runtime_us = -1 + // - let the PRU pulse a GPIO on signal_set + // - let this worker raise a GPIO while its active + // - verify with scope that PRU - signal to worker start is about 100-300us + // MUST NOT GET LARGER on any Linux activity, + + // main loop + // wait 0.1 sec, just tell + /* The prussdrv_pru_wait_event() function returns the number of times + the event has taken place, as an unsigned int. There is no out-of- + band value to indicate error (and it can wrap around to 0 if you + run the program just a whole lot of times). */ + res = prussdrv_pru_wait_event_timeout(PRU_EVTOUT_0, 100000/*us*/); +//res = prussdrv_pru_wait_event(PRU_EVTOUT_0); + // PRU may have raised more than one event before signal is accepted. + // single combination of only INIT+DATI/O possible + prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); + // uses select() internally: 0 = timeout, -1 = error, else event count received + any_event = true; + // at startup sequence, mailbox may be not yet valid + while (mailbox && res > 0 && any_event) { // res is const + ARM_DEBUG_PIN0(0); + ARM_DEBUG_PIN1(0); + ARM_DEBUG_PIN2(0); + + + any_event = false; + // Process multiple events sent by PRU. // - // Signal that the request is complete. - // - pthread_mutex_lock(&_busWorker_mutex); - dmaReq->SetComplete(); - pthread_cond_signal(&_requestFinished_cond); - pthread_mutex_unlock(&_busWorker_mutex); - } else { - // Handle interrupt request - switch (irqReq->GetInterruptLevel()) { - case 4: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4; - break; - - case 5: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5; - break; - - case 6: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6; - break; - - case 7: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7; - break; - - default: - ERROR("Request_INTR(): Illegal priority %u, aborting", - irqReq->GetInterruptLevel()); - return; + // while ARM accepts the signal, the PRU may set more events + // critical a mix of INIT and DATI/DATO: RESET and register access follow directly + // But no DATI/DATO occurs while INIT active. So reconstruct event order by + // processing order: INIT_DEASSERT, DATI/O, INIT_ASSERT, DCLO/ACLO + bool init_raising_edge = false; + bool init_falling_edge = false; + bool dclo_raising_edge = false; + bool dclo_falling_edge = false; + // DEBUG("mailbox->events: mask=0x%x", mailbox->events.eventmask); + if (mailbox->events.event_init) { + any_event = true; + // robust: any change in ACLO/DCL=INIT updates state of all 3. + // Initial DCLO-cycle to PDP_11 intialize these states + if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_INIT) { + if (!line_INIT) + init_raising_edge = true; + line_INIT = true; + } else { + if (line_INIT) + init_falling_edge = true; + line_INIT = false; + } + mailbox->events.event_init = 0; // PRU may re-raise and change mailbox now } + if (mailbox->events.event_power) { + any_event = true; + if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_DCLO) { + if (!line_DCLO) + dclo_raising_edge = true; + line_DCLO = true; + } else { + if (line_DCLO) + dclo_falling_edge = true; + line_DCLO = false; + } + mailbox->events.event_power = 0; // PRU may re-raise and change mailbox now + DEBUG( + "EVENT_INITIALIZATIONSIGNALS: (sigprev=0x%x,) cur=0x%x, init_raise=%d, init_fall=%d, dclo_raise=%d, dclo_fall=%d", + mailbox->events.initialization_signals_prev, + mailbox->events.initialization_signals_cur, init_raising_edge, + init_falling_edge, dclo_raising_edge, dclo_falling_edge); - mailbox->intr.vector = irqReq->GetVector(); - - // start! - mailbox->arm2pru_req = ARM2PRU_INTR; - // PRU now changes state - - // Signal that the request has been raised. - pthread_mutex_lock(&_busWorker_mutex); - irqReq->SetComplete(); - 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 - // do this? (as w/dma) - timeout_c timeout; - while (request_INTR_active(nullptr)) { - timeout.wait_us(50); } + if (dclo_raising_edge || dclo_falling_edge) + worker_power_event(); // power signal power change + if (init_falling_edge) // INIT asserted -> deasserted. DATI/DATO cycle only possible after that. + worker_init_event(); + if (mailbox->events.event_deviceregister) { + any_event = true; + + // DATI/DATO + // DEBUG("EVENT_DEVICEREGISTER: control=%d, addr=%06o", (int)mailbox->events.unibus_control, mailbox->events.addr); + worker_deviceregister_event(); + // ARM2PRU opcodes raised by device logic are processed in midst of bus cycle + mailbox->events.event_deviceregister = 0; // PRU continous bus cycle with SSYN now + } + if (mailbox->events.event_dma) { + any_event = true; + pthread_mutex_lock(&requests_mutex); + worker_dma_chunk_complete_event(); + pthread_mutex_unlock(&requests_mutex); + mailbox->events.event_dma = 0; // PRU may re-raise and change mailbox now + } + if (mailbox->events.event_intr) { + // INTRs are granted unpredictable by Arbitrator + any_event = true; + // INTR of which level? the .active rquest of the" + pthread_mutex_lock(&requests_mutex); + worker_intr_complete_event(mailbox->events.event_intr_level_index); + pthread_mutex_unlock(&requests_mutex); + mailbox->events.event_intr = 0; // PRU may re-raise and change mailbox now + } + if (init_raising_edge) // INIT deasserted -> asserted. DATI/DATO cycle only possible before that. + worker_init_event(); } + // Signal to PRU: continue UNIBUS cycles now with SSYN deassert } } -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()) { - dma_request_c* dmaReq = _dmaRequests.front(); - dmaReq->SetSuccess(false); - dmaReq->SetComplete(); - pthread_cond_signal(&_requestFinished_cond); - _dmaRequests.pop(); - } - while (!_irqRequests.empty()) { - irq_request_c* irqReq = _irqRequests.front(); - irqReq->SetComplete(); - pthread_cond_signal(&_requestFinished_cond); - _irqRequests.pop(); - } - 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); - - 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); - - // - // And we're done. - // -} - // debugging: print PRU sharead regsster map void unibusadapter_c::print_shared_register_map() { unsigned i; @@ -781,7 +1043,7 @@ void unibusadapter_c::print_shared_register_map() { } } -// dump known devices + // dump known devices printf("Registered devices by handle:\n"); for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) if (devices[device_handle] != NULL) { diff --git a/10.01_base/2_src/arm/unibusadapter.hpp b/10.01_base/2_src/arm/unibusadapter.hpp index 94d6080..102d9ce 100644 --- a/10.01_base/2_src/arm/unibusadapter.hpp +++ b/10.01_base/2_src/arm/unibusadapter.hpp @@ -1,6 +1,6 @@ /* unibusadapter.hpp: connects multiple "unibusdevices" to the PRU UNIBUS interface - Copyright (c) 2018, Joerg Hoppe + Copyright (c) 2018-2019 Joerg Hoppe j_hoppe@t-online.de, www.retrocmp.com Permission is hereby granted, free of charge, to any person obtaining a @@ -21,98 +21,49 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + jul-2019 JH rewrite: multiple parallel arbitration levels 12-nov-2018 JH entered beta phase */ #ifndef _UNIBUSADAPTER_HPP_ #define _UNIBUSADAPTER_HPP_ -#include -#include - #include "iopageregister.h" +#include "priorityrequest.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" -class dma_request_c { +// for each priority arbitration level, theres a table with backplane slots. +// Each device sits in a slot, the slot determinss the request priority within one level (BR4567,NP). +class priority_request_level_c { public: - dma_request_c(uint8_t unibus_control, uint32_t unibus_addr, uint16_t *buffer, - uint32_t wordcount); + // remember for each backplane slot wether the device has requested + // INTR or DMA at this level + priority_request_c* slot_request[PRIORITY_SLOT_COUNT + 1]; + // Optimization to find the high priorized slot in use very fast. + // bit array: bit set -> slot has open request. + uint32_t slot_request_mask; - ~dma_request_c(); + priority_request_c* active; // request currently handled by PRU, not in table anymore - uint8_t GetUnibusControl() { - return _unibus_control; - } - uint32_t GetUnibusStartAddr() { - return _unibus_start_addr; - } - uint16_t* GetBuffer() { - return _buffer; - } - uint32_t GetWordCount() { - return _wordcount; - } - uint32_t GetUnibusEndAddr() { - return _unibus_end_addr; - } - void SetUnibusEndAddr(uint32_t end) { - _unibus_end_addr = end; - } - - bool IsComplete() { - return _isComplete; - } - bool GetSuccess() { - return _success; - } - - void SetComplete() { - _isComplete = true; - } - void SetSuccess(bool success) { - _success = success; - } - -private: - - uint8_t _unibus_control; - uint32_t _unibus_start_addr; - uint32_t _unibus_end_addr; - uint16_t* _buffer; - uint32_t _wordcount; - - bool _isComplete; - bool _success; -}; - -class irq_request_c { -public: - irq_request_c(uint32_t level, uint32_t vector); - - ~irq_request_c(); - - uint32_t GetInterruptLevel() { - return _level; - } - uint32_t GetVector() { - return _vector; - } - bool IsComplete() { - return _isComplete; - } - - void SetComplete() { - _isComplete = true; - } - -private: - uint32_t _level; - uint32_t _vector; - bool _isComplete; + void clear(); }; // is a device_c. need a thread (but no params) class unibusadapter_c: public device_c { +private: + + // handle arbitration for each of the 5 request levels in parallel + priority_request_level_c request_levels[PRIORITY_LEVEL_COUNT]; + + pthread_mutex_t requests_mutex; + + void worker_init_event(void); + void worker_power_event(void); + void worker_deviceregister_event(void); + void worker_dma_chunk_complete_event(void); + void worker_intr_complete_event(uint8_t level_index); + void worker(unsigned instance) override; // background worker function public: unibusadapter_c(); @@ -130,33 +81,29 @@ public: 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(unsigned instance) override; // background worker function - void dma_worker(void); // background DMA worker - bool register_device(unibusdevice_c& device); void unregister_device(unibusdevice_c& device); - bool request_DMA_active(const char *error_info); - bool request_INTR_active(const char *error_info); + // Helper for request processing + void requests_init(void); - 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 request_schedule(priority_request_c& request); + void requests_cancel_scheduled(void); + priority_request_c *request_activate_lowest_slot(unsigned level_index); +// bool request_is_active( unsigned level_index); + bool request_is_blocking_active(uint8_t level_index); + void request_active_complete(unsigned level_index); + void request_execute_active_on_PRU(unsigned level_index); + + void DMA(dma_request_c& dma_request, bool blocking, uint8_t unibus_control, + uint32_t unibus_addr, uint16_t *buffer, uint32_t wordcount); + void INTR(intr_request_c& intr_request, unibusdevice_register_t *interrupt_register, + uint16_t interrupt_register_value); + void cancel_INTR(intr_request_c& intr_request) ; + void print_shared_register_map(void); -private: - - std::queue _dmaRequests; - std::queue _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 diff --git a/10.01_base/2_src/arm/unibusdevice.cpp b/10.01_base/2_src/arm/unibusdevice.cpp index 75564e7..9be9cf4 100644 --- a/10.01_base/2_src/arm/unibusdevice.cpp +++ b/10.01_base/2_src/arm/unibusdevice.cpp @@ -31,6 +31,8 @@ */ //#include //using namespace std; +#include +#include #include "logger.hpp" #include "unibusadapter.hpp" #include "unibusdevice.hpp" @@ -44,6 +46,7 @@ unibusdevice_c::unibusdevice_c() : intr_vector.readonly = false; intr_level.readonly = false; default_base_addr = 0; + default_priority_slot = 0; default_intr_vector = 0; default_intr_level = 0; @@ -60,6 +63,7 @@ bool unibusdevice_c::on_param_changed(parameter_c *param) { if (enabled.new_value) { // enable: lock UNIBUS config base_addr.readonly = true; + priority_slot.readonly = true; intr_vector.readonly = true; intr_level.readonly = true; install(); // visible on UNIBUS @@ -67,6 +71,7 @@ bool unibusdevice_c::on_param_changed(parameter_c *param) { // disable uninstall(); base_addr.readonly = false; + priority_slot.readonly = false; intr_vector.readonly = false; intr_level.readonly = false; } @@ -75,14 +80,15 @@ bool unibusdevice_c::on_param_changed(parameter_c *param) { } // 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) { +void unibusdevice_c::set_default_bus_params(uint32_t default_base_addr, unsigned priority_slot, + unsigned default_intr_vector, unsigned default_intr_level) { + assert(priority_slot <= PRIORITY_SLOT_COUNT); // bitmask! this->default_base_addr = this->base_addr.value = default_base_addr; - this->default_intr_vector = this->intr_vector.value = default_intr_vector ; - this->default_intr_level = this->intr_level.value = default_intr_level ; + this->default_priority_slot = this->intr_vector.value = priority_slot; + this->default_intr_vector = this->intr_vector.value = default_intr_vector; + this->default_intr_level = this->intr_level.value = default_intr_level; } - - void unibusdevice_c::install(void) { unibusadapter->register_device(*this); // -> device_c ? // now has handle @@ -160,17 +166,6 @@ void unibusdevice_c::reset_unibus_registers() { } } -// set an UNIBUS interrupt condition with intr_vector and intr_level -void unibusdevice_c::interrupt(unsigned vector, unsigned level) { - unibusadapter->request_INTR(level, vector); -} - -void unibusdevice_c::interrupt(void) { - // delegate to unibusadapter_c - unibusadapter->request_INTR(intr_level.value, intr_vector.value); - // WARNING("unibusdevice_c::interrupt() TODO: generated interrupt!"); -} - // log register state changes: // print event info // - print full register content to logger @@ -216,3 +211,89 @@ void unibusdevice_c::log_register_event(const char *change_info, } DEBUG(buffer); } + +// search device in global list mydevices[] +unibusdevice_c *unibusdevice_c::find_by_request_slot(uint8_t priority_slot) { + list::iterator devit; + for (devit = device_c::mydevices.begin(); devit != device_c::mydevices.end(); ++devit) { + unibusdevice_c *ubdevice = dynamic_cast(*devit); + if (ubdevice) { + // all dma and intr requests + for (vector::iterator reqit = ubdevice->dma_requests.begin(); + reqit < ubdevice->dma_requests.end(); reqit++) + if ((*reqit)->get_priority_slot() == priority_slot) + return ubdevice; + for (vector::iterator reqit = ubdevice->intr_requests.begin(); + reqit < ubdevice->intr_requests.end(); reqit++) + if ((*reqit)->get_priority_slot() == priority_slot) + return ubdevice; + } + } + return NULL; +} + +// returns a string of form +// reg_first-reg_last, slots from-to, DMA, INTR level1/vec1,level2/vec2,... +char *unibusdevice_c::get_unibus_resource_info(void) { + static char buffer[1024]; + char tmpbuff[256]; + buffer[0] = 0; + + // get register address range + // use parameter "base_addr", register struct only valid after unibusadapter.install() + if (register_count == 0) // cpu is a device without register interface + strcpy(tmpbuff, ""); + else if (register_count == 1) + sprintf(tmpbuff, "addr %06o", base_addr.value); + else + sprintf(tmpbuff, "addr %06o-%06o (%d regs)", base_addr.value, base_addr.value+2*(register_count-1), register_count) ; + strcat(buffer, tmpbuff); + + // get priority slot range from DMA request and intr_requests + uint8_t slot_from = 0xff, slot_to = 0; + for (vector::iterator it = dma_requests.begin(); it < dma_requests.end(); + it++) { + slot_from = std::min(slot_from, (*it)->get_priority_slot()); + slot_to = std::max(slot_to, (*it)->get_priority_slot()); + } + for (vector::iterator it = intr_requests.begin(); + it < intr_requests.end(); it++) { + slot_from = std::min(slot_from, (*it)->get_priority_slot()); + slot_to = std::max(slot_to, (*it)->get_priority_slot()); + } + + if (slot_from > slot_to) // no requests: use devcie parameter + slot_from = slot_to = priority_slot.value; + if (slot_from == slot_to) + sprintf(tmpbuff, ", slot %u", (unsigned) slot_from); + else + sprintf(tmpbuff, ", slots %u-%u", (unsigned) slot_from, (unsigned) slot_to); + strcat(buffer, tmpbuff); + + // DMA channels + if (dma_requests.size() > 0) { + if (dma_requests.size() == 1) + sprintf(tmpbuff, ", DMA"); + else + sprintf(tmpbuff, ", %uxDMA", dma_requests.size()); + strcat(buffer, tmpbuff); + } + // Interrupts + if (intr_requests.size() > 4) { + // that crazy testcontroller has 31*4 ! + sprintf(tmpbuff, "%d INTRs", intr_requests.size()); + strcat(buffer, tmpbuff); + } else if (intr_requests.size() > 0) { + const char *sep = ":"; + strcat(buffer, ", INTRs"); + for (vector::iterator it = intr_requests.begin(); + it < intr_requests.end(); it++) { + sprintf(tmpbuff, "%s%d/%03o", sep, (*it)->get_level(), (*it)->get_vector()); + strcat(buffer, tmpbuff); + sep = ","; + } + } + + return buffer; +} + diff --git a/10.01_base/2_src/arm/unibusdevice.hpp b/10.01_base/2_src/arm/unibusdevice.hpp index 4d43e95..33dc5c8 100644 --- a/10.01_base/2_src/arm/unibusdevice.hpp +++ b/10.01_base/2_src/arm/unibusdevice.hpp @@ -33,9 +33,12 @@ #include "device.hpp" #include "iopageregister.h" +#include "priorityrequest.hpp" +// forwards class unibusdevice_c; -typedef struct { + +typedef struct unibusdevice_register_struct { // backlink unibusdevice_c *device; char name[40]; // for display @@ -77,33 +80,49 @@ typedef struct { } unibusdevice_register_t; class unibusdevice_c: public device_c { +public: + static unibusdevice_c *find_by_request_slot(uint8_t priority_slot) ; + private: // setup address tables, also in shared memory // start both threads - void install(void); + void install(void); - void uninstall(void); - bool is_installed() { + void uninstall(void);bool is_installed() { return (handle > 0); } public: uint8_t handle; // assigned by "unibus.adapter.register + // !!! slot, vector, level READONLY. If user shoudl change, + // !! add logic to update dma_request_c and intr_request_c + // 0 = not "Plugged" in to UNIBUS parameter_unsigned_c base_addr = parameter_unsigned_c(this, "base_addr", "addr", true, "", "%06o", "controller base address in IO page", 18, 8); + parameter_unsigned_c priority_slot = parameter_unsigned_c(this, "slot", "sl", true, "", + "%d", "backplane slot #, interrupt priority within one level, 0 = next to CPU", 16, + 10); + // dump devices without buffers toward CPU, smart buffering devices other end + parameter_unsigned_c intr_vector = parameter_unsigned_c(this, "intr_vector", "iv", true, "", "%03o", "interrupt vector address", 9, 8); parameter_unsigned_c intr_level = parameter_unsigned_c(this, "intr_level", "il", true, "", - "%o", "interrupt bus request level", 3, 8); + "%o", "interrupt bus request level: 4,5,6,7", 3, 8); // DEC defaults as defined by device type uint32_t default_base_addr; - unsigned default_intr_vector; - unsigned default_intr_level; - void set_default_bus_params(uint32_t default_base_addr, unsigned default_intr_vector, unsigned default_intr_level) ; - + uint8_t default_priority_slot; + uint8_t default_intr_level; + uint16_t default_intr_vector; + + // requests in use + std::vector dma_requests; + std::vector intr_requests; + + void set_default_bus_params(uint32_t default_base_addr, unsigned default_priority_slot, + unsigned default_intr_vector, unsigned default_intr_level); // controller register data as pointer to registers in shared PRU RAM // UNIBUS addr of register[i] = base_addr + 2*i @@ -131,10 +150,6 @@ public: unibusdevice_register_t *register_by_name(string name); unibusdevice_register_t *register_by_unibus_address(uint32_t addr); - // set an UNIBUS interrupt condition with parameters intr_vector and intr_level - void interrupt(void); - void interrupt(unsigned vector, unsigned level) ; - // callback to be called on controller register DATI/DATO events. // must ACK mailbox.event.signal. Asynchron! // May not generate direct INTR or DMA. @@ -149,6 +164,8 @@ public: void log_register_event(const char *change_info, unibusdevice_register_t *changed_reg); + char *get_unibus_resource_info(void) ; + }; #endif diff --git a/10.01_base/2_src/arm/utils.cpp b/10.01_base/2_src/arm/utils.cpp index 122f269..5305c92 100644 --- a/10.01_base/2_src/arm/utils.cpp +++ b/10.01_base/2_src/arm/utils.cpp @@ -1,29 +1,29 @@ /* utils.cpp: misc. utilities - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase - 20-may-2018 JH created -*/ + 12-nov-2018 JH entered beta phase + 20-may-2018 JH created + */ #define _UTILS_CPP_ @@ -31,8 +31,10 @@ #include #include #include -#include +#include #include +#include +#include #include #include #include @@ -46,13 +48,12 @@ using namespace std; - /********************************* * strcpy without buffer overlfow */ void strcpy_s(char *dest, int len, const char *src) { - strncpy(dest, src, len-1) ; - dest[len-1] = 0 ; // termiante if truncated + strncpy(dest, src, len - 1); + dest[len - 1] = 0; // termiante if truncated } /********************************* @@ -72,16 +73,22 @@ void SIGINTcatchnext() { SIGINTreceived = 0; } -void break_here(void) {} - +void break_here(void) { +} /*** time measuring ***/ - timeout_c::timeout_c() { - log_label = "TO" ; + log_label = "TO"; } +uint64_t timeout_c::get_resolution_ns() { + struct timespec res; + clock_getres(CLOCK_MONOTONIC, &res); + return BILLION * res.tv_sec + res.tv_nsec ; +} + + void timeout_c::start(uint64_t duration_ns) { this->duration_ns = duration_ns; clock_gettime(CLOCK_MONOTONIC, &starttime); @@ -90,9 +97,19 @@ void timeout_c::start(uint64_t duration_ns) { uint64_t timeout_c::elapsed_ns(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - return BILLION * (now.tv_sec - starttime.tv_sec) + now.tv_nsec - starttime.tv_nsec; + uint64_t result = (uint64_t)BILLION * (now.tv_sec - starttime.tv_sec) + (uint64_t)now.tv_nsec - starttime.tv_nsec; + return result ; } +uint64_t timeout_c::elapsed_us(void) { + return elapsed_ns() / 1000 ; +} + +uint64_t timeout_c::elapsed_ms(void) { + return elapsed_ns() / MILLION ; +} + + bool timeout_c::reached() { return (elapsed_ns() > duration_ns); } @@ -102,7 +119,7 @@ void timeout_c::wait_ns(uint64_t duration_ns) { struct timespec ts = { (long) (duration_ns / BILLION), (long) (duration_ns % BILLION) }; int res = nanosleep(&ts, NULL); if (res) - DEBUG("nanosleep() return a %d", res) ; + DEBUG("nanosleep() return a %d", res); } // wait a number of milliseconds @@ -126,24 +143,68 @@ void progress_c::init(unsigned linewidth) { } void progress_c::put(const char *info) { - printf("%s", info); cur_col += strlen(info); if (cur_col >= linewidth) { printf("\n"); - cur_col = 0; + cur_col = strlen(info); } + printf("%s", info); fflush(stdout); } +void progress_c::putf(const char *fmt, ...) { + static char buffer[256]; + va_list arg_ptr; + + va_start(arg_ptr, fmt); + vsprintf(buffer, fmt, arg_ptr); + va_end(arg_ptr); + + put(buffer); +} /* random number with 24 valid bits * RAND_MAX is only guaranteed 15 bits */ unsigned random24() { unsigned val; + assert(RAND_MAX >= 0x3fff); val = rand() ^ (rand() << 9); return val & 0xffffff; } +/* random numbers, distributed logarithmically + * returns 0..limit-1 + */ +uint32_t random32_log(uint32_t limit) { + uint32_t result, mantissa; + int rand_exponent, limit_exp; + assert(limit > 0); + assert(RAND_MAX >= 0x3fff); // 15 bits + // generate normalized mantissa, bit 31 set + mantissa = rand(); + mantissa ^= (rand() << 9); + mantissa ^= (rand() << 18); + while ((mantissa & (1 << 31)) == 0) + mantissa <<= 1; + // rand_exponent of limit: 2^limit_exp <= limit + // ctz = "Count Leading Zeros" + // limit = 1 -> exp=0, limit = 0xffffffff -> exp = 31 + limit_exp = 31 - __builtin_clz(limit); + limit_exp++; // 2^limit_exp >= limit + + // random rand_exponent 0..limit-1 + rand_exponent = rand() % limit_exp; + // 2^rand_exponent <= limit + result = mantissa >> (31 - rand_exponent); + // mantissa has bit 31 set, is never shifted more then 31 + assert(result); + + // final masking + if (limit > 1) + result %= limit; + return result; +} + char *cur_time_text() { static char result[80], millibuff[10]; timeval cur_time; @@ -166,17 +227,16 @@ bool fileExists(const std::string& filename) { // Generates "perror()" printout, // msgfmt must have one "%s" field for absolute filename char *fileErrorText(const char *msgfmt, const char *fname) { - static char linebuff[PATH_MAX +100]; - char abspath[PATH_MAX] ; + static char linebuff[PATH_MAX + 100]; + char abspath[PATH_MAX]; realpath(fname, abspath); sprintf(linebuff, msgfmt, abspath); - strcat(linebuff, ": ") ; - strcat (linebuff, strerror(errno)) ; + strcat(linebuff, ": "); + strcat(linebuff, strerror(errno)); // perror(linebuff); - return linebuff ; + return linebuff; } - // add a number of microseconds to a time struct timespec timespec_add_us(struct timespec ts, unsigned us) { ts.tv_nsec += us * 1000; diff --git a/10.01_base/2_src/arm/utils.hpp b/10.01_base/2_src/arm/utils.hpp index 2819ed9..50321ed 100644 --- a/10.01_base/2_src/arm/utils.hpp +++ b/10.01_base/2_src/arm/utils.hpp @@ -1,29 +1,29 @@ /* utils.hpp: misc. utilities - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase - 20-may-2018 JH created -*/ + 12-nov-2018 JH entered beta phase + 20-may-2018 JH created + */ #ifndef _UTILS_HPP_ #define _UTILS_HPP_ @@ -42,14 +42,12 @@ #define BIT(n) (1 << (n)) - #ifndef _UTILS_CPP_ extern volatile int SIGINTreceived; #endif - // my version -void strcpy_s(char *dest, int len, const char *src) ; +void strcpy_s(char *dest, int len, const char *src); // mark unused parameters #define UNUSED(x) (void)(x) @@ -59,23 +57,26 @@ void strcpy_s(char *dest, int len, const char *src) ; void SIGINTcatchnext(); // dummy to have an executable line for break points -void break_here(void) ; +void break_here(void); class timeout_c: public logsource_c { private: struct timespec starttime; uint64_t duration_ns; public: - timeout_c() ; + timeout_c(); + uint64_t get_resolution_ns(void) ; void start(uint64_t duration_ns); - uint64_t elapsed_ns(void);bool reached(void); + uint64_t elapsed_ns(void); + uint64_t elapsed_us(void); + uint64_t elapsed_ms(void); + bool reached(void); void wait_ns(uint64_t duration_ns); void wait_us(unsigned duration_us); void wait_ms(unsigned duration_ms); }; - class progress_c { private: unsigned linewidth; @@ -84,27 +85,28 @@ public: progress_c(unsigned linewidth); void init(unsigned linewidth); void put(const char *info); + void putf(const char *fmt, ...); + }; -unsigned random24(); +unsigned random24(void); +uint32_t random32_log(uint32_t limit); -char *cur_time_text(void) ; +char *cur_time_text(void); // remove leading/trailing spaces // https://stackoverflow.com/questions/83439/remove-spaces-from-stdstring-in-c #define TRIM_STRING(str) str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end()) -bool fileExists(const std::string& filename) ; +bool fileExists(const std::string& filename); -char * fileErrorText(const char *msgfmt, const char *fname) ; +char * fileErrorText(const char *msgfmt, const char *fname); //ool caseInsCompare(const std::string& s1, const std::string& s2) ; - // add a number of microseconds to a time -struct timespec timespec_add_us(struct timespec ts, unsigned us) ; +struct timespec timespec_add_us(struct timespec ts, unsigned us); // add microseconds to current time -struct timespec timespec_future_us(unsigned offset_us) ; - +struct timespec timespec_future_us(unsigned offset_us); #endif /* _UTILS_H_ */ diff --git a/10.01_base/2_src/pru1/pru1_arm_mailbox.c b/10.01_base/2_src/pru1/pru1_arm_mailbox.c index 922386a..1224dec 100644 --- a/10.01_base/2_src/pru1/pru1_arm_mailbox.c +++ b/10.01_base/2_src/pru1/pru1_arm_mailbox.c @@ -1,38 +1,37 @@ /* pru1_arm_mailbox.c: datastructures common to ARM and PRU - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #include #define _MAILBOX_C_ #include "mailbox.h" - // Place PRU-ARM mailbox struct at begin of PRU_SHAREDMEM // (shared 12KB memory) #pragma DATA_SECTION(mailbox,".mailbox_arm_sec") -volatile far mailbox_t mailbox ; +volatile far mailbox_t mailbox; diff --git a/10.01_base/2_src/pru1/pru1_buslatches.c b/10.01_base/2_src/pru1/pru1_buslatches.c index 7250c74..d1f5eef 100644 --- a/10.01_base/2_src/pru1/pru1_buslatches.c +++ b/10.01_base/2_src/pru1/pru1_buslatches.c @@ -352,8 +352,6 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { // spruh73n, chapter 4.4.1.2.3.2, CT_CFG.GPCFG1_bit.PRU1_GPI_MODE = 0; - DEBUG_PIN_SET(0); // clear, no error - #ifdef TEST_66MHZ while (1) { __R30 |= (1 << 12); // set PRU1.12 @@ -396,16 +394,16 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { uint8_t resvar; // echo DATA0 read only buslatches_test_get(2,resvar); - DEBUG_PIN_SET(buslatches_getbyte(2) != a); + PRU_DEBUG_PIN(buslatches_getbyte(2) != a); // buslatches_debug_set(resvar & 1); buslatches_test_get(3,resvar); - DEBUG_PIN_SET(buslatches_getbyte(3) != b); + PRU_DEBUG_PIN(buslatches_getbyte(3) != b); //buslatches_debug_set(resvar & 1); buslatches_test_get(5,resvar); - DEBUG_PIN_SET(buslatches_getbyte(5) != c); + PRU_DEBUG_PIN(buslatches_getbyte(5) != c); //buslatches_debug_set(resvar & 1); buslatches_test_get(6,resvar); - DEBUG_PIN_SET(buslatches_getbyte(6) != d); + PRU_DEBUG_PIN(buslatches_getbyte(6) != d); //buslatches_debug_set(resvar & 1); } #endif @@ -423,16 +421,16 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { buslatches_setbyte(5, c) ; if (buslatches_getbyte(2) != a) - DEBUG_PIN_PULSE_100NS + PRU_DEBUG_PIN_PULSE_100NS ;// show error flag. cleared by next reg_sel buslatches_setbyte(6, d) ; if (buslatches_getbyte(3) != b) - DEBUG_PIN_PULSE_100NS; + PRU_DEBUG_PIN_PULSE_100NS; if (buslatches_getbyte(5) != c) - DEBUG_PIN_PULSE_100NS; + PRU_DEBUG_PIN_PULSE_100NS; if (buslatches_getbyte(6) != d) - DEBUG_PIN_PULSE_100NS; + PRU_DEBUG_PIN_PULSE_100NS; a++; b++; c++; diff --git a/10.01_base/2_src/pru1/pru1_buslatches.h b/10.01_base/2_src/pru1/pru1_buslatches.h index 793bc14..2a2d8b7 100644 --- a/10.01_base/2_src/pru1/pru1_buslatches.h +++ b/10.01_base/2_src/pru1/pru1_buslatches.h @@ -69,7 +69,6 @@ extern buslatches_t buslatches; (__R31 & 0xff) \ ) - // identify register which must be set byte-wise #define BUSLATCHES_REG_IS_BYTE(reg_sel) ( \ ((reg_sel) == 2) || ((reg_sel) == 3) || ((reg_sel) == 5) || ((reg_sel) == 6) \ @@ -133,7 +132,7 @@ void buslatches_reset(void); void buslatches_powercycle(void); -void buslatches_exerciser(void) ; +void buslatches_exerciser(void); void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d); diff --git a/10.01_base/2_src/pru1/pru1_buslatches_overlapping.txt b/10.01_base/2_src/pru1/pru1_buslatches_overlapping.txt deleted file mode 100644 index 2005a2c..0000000 --- a/10.01_base/2_src/pru1/pru1_buslatches_overlapping.txt +++ /dev/null @@ -1,52 +0,0 @@ -Speed up busaltch access with overlapping access -to 3:8 mux -On read: -macro buslatches_get(cycles_since_regmux, reg_sel, reg_sel_next, result_var, reg_sel_current_var) - -R1 - check if 74138 regmux is already set as expected (vvariable "reg_sel_current") -if yes: - R21 wait until required delay to last mux switch is reached - Cycle time since last access to regmux_sel in "cycles_since_regmux") - (read_ 10 cycles, 6 alone for BBB signal processing) -if no: - R31 - set mux - R32 wait full delay (10 cycles) -R22- read in muxed data from lvt541 - result_var := R31 -R4- set next mux expected value "reg_sel_next" -R5 update reg_sel_current - - -On write: macro buslatches_set(cycles_since_regmux, reg_sel, reg_sel_next, val, reg_sel_current_variable) -W1 - setup data do PRU0 outputs -W2 - set regmux, update reg_sel_current -Dont check if 74138 regmux is already set as exepected, -Setting of output data on PRU0 uses moretime than set up uf regmux and -progation of selct singals to 74371 latches (only 10ns!) - - - -Code path: -- check needed, if reg_mux is as set by previous busaltch_get/set() -- checks static, may be elimianted by compiler. -- if reg_sel_next is a local var, it may be eliminated totally -- if reg_sel_next is a global var, expected "next" value in different state machnies - may be read from logic analyzer trace of "typical" operation. - - a local variable "cycles_since_last_regmux" may be used to - count processing time. It is only set with const vlaues, - so may be elimated by compiler and may be used in delay_cycles() - - -uint8_t reg_sel_current ; // local var - reg_sel_current = 0xff - -a0_7 = buslatches_get(0, 4, 5, reg_sel_current) ; -// => if (4 != 0xff) -> code R31,R32, wait 10 cycles - - -a8_15 = buslatches_get(4, 5, 2, reg_sel_current) ; -if (5 != 5) => mux already correct, - R21: delaycycles(10-4), // estimated 4 cycles processing time - - - diff --git a/10.01_base/2_src/pru1/pru1_ddrmem.c b/10.01_base/2_src/pru1/pru1_ddrmem.c index 9ffcad3..922ec3c 100644 --- a/10.01_base/2_src/pru1/pru1_ddrmem.c +++ b/10.01_base/2_src/pru1/pru1_ddrmem.c @@ -1,28 +1,28 @@ /* pru1_ddrmem.c: Control the shared DDR RAM - used for UNIBUS memory - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #include #include @@ -33,8 +33,8 @@ // fill whole memory with pattern // called by mailbox command ARM2PRU_REQUEST_DDR_FILL_PATTERN void ddrmem_fill_pattern(void) { - unsigned n ; + unsigned n; volatile uint16_t *wordaddr = mailbox.ddrmem_base_physical->memory.words; - for (n = 0; n < UNIBUS_WORDCOUNT ; n++) - *wordaddr++ = n ; + for (n = 0; n < UNIBUS_WORDCOUNT; n++) + *wordaddr++ = n; } diff --git a/10.01_base/2_src/pru1/pru1_iopageregisters.c b/10.01_base/2_src/pru1/pru1_iopageregisters.c index e53e3d2..9df2970 100644 --- a/10.01_base/2_src/pru1/pru1_iopageregisters.c +++ b/10.01_base/2_src/pru1/pru1_iopageregisters.c @@ -1,28 +1,28 @@ /* pru1_iopageregisters.c: handle UNIBUS behaviour of emulated devices - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #define _IOPAGEREGISTERS_C_ @@ -30,7 +30,7 @@ #include #include "pru1_utils.h" -#include "pru1_buslatches.h" // DEBUG_PIN_SET +#include "pru1_buslatches.h" // PRU_DEBUG_PIN #include "mailbox.h" #include "iopageregister.h" #include "ddrmem.h" @@ -42,7 +42,6 @@ // not volatile: data seldom changed by ARM, speed matters! iopageregisters_t deviceregisters; - /* request value from a device register * page_table_entry already calculated for addr * may have side effects onto other registers! @@ -52,26 +51,27 @@ iopageregisters_t deviceregisters; * for post processing. SSYN must remain asserted until ARM is complete */ uint8_t iopageregisters_read(uint32_t addr, uint16_t *val) { - uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr); + uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr); if (page_table_entry == PAGE_MEMORY) { // addr in allowed 18bit memory range, not in I/O page *val = DDRMEM_MEMGET_W(addr); return 1; } else if (page_table_entry == PAGE_IO) { // uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)]; - uint8_t reghandle ; - reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ; + uint8_t reghandle; + reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr); if (!reghandle) { return 0; // register not implemented as "active" - } + } // return register value. remove "volatile" attribute -// DEBUG_PIN_SET(1) ; - // indexing this records takes 4,6 us, if record size != 8 + // indexing this records takes 4,6 us, if record size != 8 iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias -// DEBUG_PIN_SET(0) ; *val = reg->value; if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATI) DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATI, addr, *val); + // ARM is clearing this, while SSYN asserted, so no concurrent next bus cycle. + // no concurrent ARP+PRU access + return 1; } else return 0; @@ -83,7 +83,7 @@ uint8_t iopageregisters_read(uint32_t addr, uint16_t *val) { * may set mailbox event to ARM, then SSYN must remain asserted until ARM is complete */ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) { - uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr); + uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr); if (page_table_entry == PAGE_MEMORY) { // addr in allowed 18bit memory range, not in I/O page // no check wether addr is even (A00=0) @@ -92,13 +92,13 @@ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) { return 1; } else if (page_table_entry == PAGE_IO) { // uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)]; - uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ; + uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr); if (!reghandle) return 0; // register not implemented // change register value iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias uint16_t reg_val = (reg->value & ~reg->writable_bits) | (w & reg->writable_bits); - reg->value = reg_val ; + reg->value = reg_val; if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATO) DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATO, addr, reg_val); return 1; @@ -107,29 +107,29 @@ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) { } uint8_t iopageregisters_write_b(uint32_t addr, uint8_t b) { - uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr); + uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr); if (page_table_entry == PAGE_MEMORY) { // addr in allowed 18bit memory range, not in I/O page DDRMEM_MEMSET_B(addr, b); return 1; } else if (page_table_entry == PAGE_IO) { // uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)]; - uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ; + uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr); if (!reghandle) return 0; // register not implemented // change register value iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias - uint16_t reg_val ; + uint16_t reg_val; if (addr & 1) // odd address = write upper byte reg_val = (reg->value & 0x00ff) // don't touch lower byte | (reg->value & ~reg->writable_bits & 0xff00) // protected upper byte bits - | (((uint16_t)b << 8) & reg->writable_bits); // changed upper byte bits + | (((uint16_t) b << 8) & reg->writable_bits); // changed upper byte bits else // even address: write lower byte reg_val = (reg->value & 0xff00) // don' touch upper byte | (reg->value & ~reg->writable_bits & 0x00ff) // protected upper byte bits | (b & reg->writable_bits); // changed lower byte bits - reg->value = reg_val ; + reg->value = reg_val; if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATO) DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATOB, addr, reg_val); return 1; diff --git a/10.01_base/2_src/pru1/pru1_main_test.c b/10.01_base/2_src/pru1/pru1_main_test.c index d254d1d..a698e93 100644 --- a/10.01_base/2_src/pru1/pru1_main_test.c +++ b/10.01_base/2_src/pru1/pru1_main_test.c @@ -38,6 +38,7 @@ #include #include +#include #include #include "resource_table_empty.h" @@ -57,9 +58,9 @@ #include "pru1_statemachine_init.h" #include "pru1_statemachine_powercycle.h" -// supress warnigns about using void * as fucntion pinters -// sm_slave_state = (statemachine_state_func)&sm_slave_start; - // while (sm_slave_state = sm_slave_state()) << usage +// Supress warnings about using void * as function pointers +// sm_slave_state = (statemachine_state_func)&sm_slave_start; +// while (sm_slave_state = sm_slave_state()) << usage #pragma diag_push #pragma diag_remark=515 @@ -68,7 +69,7 @@ void main(void) { /* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; - timeout_init() ; + timeout_init(); // clear all tables, as backup if ARM fails todo iopageregisters_init(); @@ -76,10 +77,7 @@ void main(void) { buslatches_reset(); // all deasserted // init mailbox - mailbox.arm2pru_req = ARM2PRU_NONE; - mailbox.events.eventmask = 0; - mailbox.events.initialization_signals_prev = 0; - mailbox.events.initialization_signals_cur = 0; + memset((void *) &mailbox, 0, sizeof(mailbox)); while (1) { // display opcode (active for one cycle @@ -107,7 +105,7 @@ void main(void) { __halt(); // that's it break; #ifdef USED - case ARM2PRU_MAILBOXTEST1: + case ARM2PRU_MAILBOXTEST1: // simulate a register read access. #ifdef TEST_TIMEOUT while (1) { @@ -128,8 +126,8 @@ void main(void) { // pru_pru_mailbox.pru0_r30 = mailbox.mailbox_test.addr & 0xff; // __R30 = (mailbox.mailbox_test.addr & 0xf) << 8; mailbox.mailbox_test.val = mailbox.mailbox_test.addr; - __R30 = (mailbox.arm2pru_req & 0xf) << 8; // optical ACK - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + __R30 = (mailbox.arm2pru_req & 0xf) << 8;// optical ACK + mailbox.arm2pru_req = ARM2PRU_NONE;// ACK: done break; #endif case ARM2PRU_BUSLATCH_INIT: // set all mux registers to "neutral" @@ -159,10 +157,10 @@ void main(void) { mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; } - case ARM2PRU_BUSLATCH_EXERCISER: // exercise 8 byte accesses to mux registers - buslatches_exerciser() ; - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break ; + case ARM2PRU_BUSLATCH_EXERCISER: // exercise 8 byte accesses to mux registers + buslatches_exerciser(); + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; case ARM2PRU_BUSLATCH_TEST: { buslatches_test(mailbox.buslatch_test.addr_0_7, mailbox.buslatch_test.addr_8_15, @@ -171,33 +169,33 @@ void main(void) { break; } case ARM2PRU_INITPULSE: // generate a pulse on UNIBUS INIT - // INIT: latch[7], bit 3 + // INIT: latch[7], bit 3 buslatches_setbits(7, BIT(3), BIT(3)); // assert INIT __delay_cycles(MILLISECS(250)); // INIT is 250ms buslatches_setbits(7, BIT(3), 0); // deassert INIT - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; case ARM2PRU_POWERCYCLE: // do ACLO/DCLO power cycle buslatches_powercycle(); mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; - case ARM2PRU_DMA_ARB_NONE: { + case ARM2PRU_DMA: { // without NPR/NPG arbitration - statemachine_state_func sm_dma_state = (statemachine_state_func)&sm_dma_start; + statemachine_state_func sm_dma_state = (statemachine_state_func) &sm_dma_start; // simply call current state function, until stopped // parallel the BUS-slave statemachine is triggered // by master logic. while (sm_dma_state = sm_dma_state()) ; - } + } mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; case ARM2PRU_DDR_FILL_PATTERN: ddrmem_fill_pattern(); mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; - case ARM2PRU_DDR_SLAVE_MEMORY: + case ARM2PRU_DDR_SLAVE_MEMORY: // respond to UNIBUS cycles as slave and // access DDR as UNIBUS memory. @@ -207,7 +205,8 @@ void main(void) { // do UNIBUS slave cycles, until ARM abort this by // writing into mailbox.arm2pru_req while (mailbox.arm2pru_req == ARM2PRU_DDR_SLAVE_MEMORY) { - statemachine_state_func sm_slave_state = (statemachine_state_func)&sm_slave_start; + statemachine_state_func sm_slave_state = + (statemachine_state_func) &sm_slave_start; // do all states of an access, start when MSYN found. while (sm_slave_state = sm_slave_state()) ; diff --git a/10.01_base/2_src/pru1/pru1_main_unibus.c b/10.01_base/2_src/pru1/pru1_main_unibus.c index 8ba0cef..5ec1305 100644 --- a/10.01_base/2_src/pru1/pru1_main_unibus.c +++ b/10.01_base/2_src/pru1/pru1_main_unibus.c @@ -63,33 +63,46 @@ #include "pru1_statemachine_init.h" #include "pru1_statemachine_powercycle.h" -// supress warnigns about using void * as fucntion pinters +// supress warnigns about using void * as function pointers // sm_slave_state = (statemachine_state_func)&sm_slave_start; - // while (sm_slave_state = sm_slave_state()) << usage +// while (sm_slave_state = sm_slave_state()) << usage #pragma diag_push #pragma diag_remark=515 +/*** + 3 major states executed in circular 1- 2- 3 order. -void test(){ - while(1) { - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - } -} - + 1. "SLAVE": + UniBone monitoring BUS traffic as slave for DATI/DATO cycles + Watch UNIBUS for CPU access to emulated memory/devices + High speed not necessary: Bus master will wait with MSYN if UniBone not responding. + wathcin BG/BPG signals, catching requested GRANts and forwardinf + other GRANTS + - monitorinf INIT and AC_LO/DC_LO + - watching fpr AMR2PRU commands + 2. "BBSYWAIT": UNibone got PRIORITY GRAMT, has set SACK and released BR/NPR + waits for current BUS master to relaeasy BBSY (ony DATI/DATO cycle max) + - SACK active: no GRANT forward necessary, no arbitration necessary + - INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary + 3. "MASTER": UniBone is Bus master: transfering INTR vector or doing DMA + - Own timing: transfer DMA data block or INTR vector with master cycles + - SACK active: no GRANT forward necessary, no arbitration necessary + - INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary + */ void main(void) { // state function pointer for different state machines - statemachine_state_func sm_slave_state ; - statemachine_state_func sm_arb_state ; - statemachine_state_func sm_dma_state ; - statemachine_state_func sm_intr_state ; - statemachine_state_func sm_init_state ; - statemachine_state_func sm_powercycle_state ; + statemachine_arb_worker_func sm_arb_worker = &sm_arb_worker_client; + statemachine_state_func sm_data_slave_state = NULL; + statemachine_state_func sm_data_master_state = NULL; + statemachine_state_func sm_init_state = NULL; + statemachine_state_func sm_powercycle_state = NULL; + // these are function pointers: could be 16bit on PRU? /* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; - timeout_init() ; + timeout_init(); // clear all tables, as backup if ARM fails todo iopageregisters_init(); @@ -97,153 +110,151 @@ void main(void) { buslatches_reset(); // all deasserted, caches cleared /* ARM must init mailbox, especially: - mailbox.arm2pru_req = ARM2PRU_NONE; - mailbox.events.eventmask = 0; - mailbox.events.initialization_signals_prev = 0; - mailbox.events.initialization_signals_cur = 0; - */ - - /* start parallel emulation of all devices, - * Process __DMA and _INTR bus master operations - * - * ! Several state machines (DMA, Powercycle, INIT,) use the same global timeout. - * ! Never execute these in parallel ! + mailbox.arm2pru_req = ARM2PRU_NONE; + mailbox.events.eventmask = 0; + mailbox.events.initialization_signals_prev = 0; + mailbox.events.initialization_signals_cur = 0; */ - // Reset PDP-11 with power-cycle simulation. - // Necessary, as until now NPR/NPG/BG/BR/SACK lines were "unconnected" -// buslatches_powercycle(); -// __delay_cycles(MILLISECS(100)); - // execute 2x, because M9312 boot ROMs need this - // __delay_cycles(MILLISECS(250)); - // buslatches_powercycle(); - // buslatches_pulse_debug ; + sm_arb_reset(); - // base operation: accept and execute slave cycles while (true) { - uint32_t arm2pru_req_cached; - // do all states of an access, start when MSYN found. + uint8_t arm2pru_req_cached; - // slave cycles may trigger events to ARM, which changes "active" registers - // and issues interrupts + if (sm_data_master_state == NULL) { + // State 1 "SLAVE" + // fast: a complete slave data cycle + if (!sm_data_slave_state) + sm_data_slave_state = (statemachine_state_func) &sm_slave_start; + while ((sm_data_slave_state = sm_data_slave_state()) && !mailbox.events.event_deviceregister) + // throws signals to ARM, + // Acess to interna lregsitres may may issue AMR2PRU opcode, so exit loop then + ;// execute complete slave cycle, then check NPR/INTR - sm_slave_state = (statemachine_state_func)&sm_slave_start; - while (sm_slave_state = sm_slave_state()) - ; // execute complete slave cycle, then check NPR/INTR + // one phase of INIT or power cycle + if (sm_powercycle_state) + sm_powercycle_state = sm_powercycle_state(); + else if (sm_init_state) + // init only if no power cycle, power cycle overrides INIT + sm_init_state = sm_init_state(); - // update state of init lines - // INIT never asserted in the midst of a transaction, bit 3,4,5 - do_event_initializationsignals(); + // signal INT or PWR FAIL to ARM + do_event_initializationsignals(); - // standard operation may be interrupt by other requests - arm2pru_req_cached = mailbox.arm2pru_req; - switch (arm2pru_req_cached) { - case ARM2PRU_NONE: - // pass BG[4-7] to next device, state machine "idle" - // pass all Arbitration GRANT IN to GRANT OUT for next device. - // This is not necessary while INTR or DMA is actiove: + // Priority Arbitration + // execute one of the arbitration workers + uint8_t grant_mask = sm_arb_worker(); + // sm_arb_worker()s include State 2 "BBSYWAIT". + // So now SACK maybe set, even if grant_mask is still 0 + + if (grant_mask & PRIORITY_ARBITRATION_BIT_NP) { + sm_data_master_state = (statemachine_state_func) &sm_dma_start; + // can data_master_state be overwritten in the midst of a running data_master_state ? + // no: when running, SACK is set, no new GRANTs + } else if (grant_mask & PRIORITY_ARBITRATION_INTR_MASK) { + // convert bit in grant_mask to INTR index + uint8_t idx = PRIORITY_ARBITRATION_INTR_BIT2IDX(grant_mask); + // now transfer INTR vector for interupt of GRANted level. + // vector and ARM context have been setup by ARM before ARM2PRU_INTR already + sm_intr.vector = mailbox.intr.vector[idx]; + sm_intr.level_index = idx; // to be returned to ARM on complete + + sm_data_master_state = (statemachine_state_func) &sm_intr_start; + } + } else { + // State 3 "MASTER" + // we have been GRANTed bus mastership and are doing DMA or INTR + // SACK held here -> no further arbitration // INTR is only 1 cycle, DMA has SACK set all the time, arbitration - // prohibited then. - sm_arb_state_idle(); - // do only forward GRANT lines if not INTR is pending, - // else our GRANT would be passed too. - break; // fast case: only slave operation - case ARM2PRU_NOP: // needed to probe PRU run state - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break; - case ARM2PRU_DMA_ARB_NONE: // ignore SACK condition - case ARM2PRU_DMA_ARB_MASTER: // also without arbitration, TODO! - sm_dma_state = (statemachine_state_func)&sm_dma_start; - while (sm_dma_state = sm_dma_state()) - ; - - // a dma cycle into a device register may trigger an interrupt - // do not delete that condition - if (mailbox.arm2pru_req == arm2pru_req_cached) - mailbox.arm2pru_req = ARM2PRU_NONE; // clear request - break; - case ARM2PRU_DMA_ARB_CLIENT: - // start DMA cycle - // can not run parallel with INTR levels - - sm_arb_state = (statemachine_state_func)sm_arb_start(ARBITRATION_PRIORITY_BIT_NP); - while (sm_arb_state && (sm_arb_state = sm_arb_state())) { - // sm_slave is most time critical, as it must keep track with MSYN/SSYN bus traffic. - // so give it more cpu cycles - sm_slave_state = (statemachine_state_func)&sm_slave_start; - while (sm_slave_state = sm_slave_state()) - ; + // prohibited then. + + sm_data_master_state = sm_data_master_state(); // execute only ONE state , + // else DMA blocks will block prcoessing of other state machines + // throws signals to ARM, causes may issue mailbox.arm2pru_req + } + + // process ARM commands in master and slave mode + // standard operation may be interrupt by other requests + if (arm2pru_req_cached = mailbox.arm2pru_req) { + // not ARM2PRU_NONE + switch (arm2pru_req_cached) { + case ARM2PRU_NOP: // needed to probe PRU run state + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_ARB_MODE_NONE: // ignore SACK condition + // from now on, ignore INTR requests and allow DMA request immediately + sm_arb_worker = &sm_arb_worker_none; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_ARB_MODE_CLIENT: + // request DMA from external Arbitrator + sm_arb_worker = &sm_arb_worker_client; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_ARB_MODE_MASTER: + sm_arb_worker = &sm_arb_worker_master; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_DMA: + // request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_* + sm_arb.request_mask |= PRIORITY_ARBITRATION_BIT_NP; + // sm_arb_worker() evaluates this,extern Arbitrator raises Grant, excution starts in future loop + // end of DMA is signaled to ARM with signal + + /* TODO: speed up DMA + While DMA is active: + - SACK active: no GRANT forward necessary + no arbitration necessary + - INIT is monitored: no DC_LO/INIT monitoring necessary + - no scan for new ARM2PRU commands: ARM2PRU_DMA is blocking + - smaller chunks ? + */ + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_INTR: + // request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_* + // start one INTR cycle. May be raised in midst of slave cycle + // by ARM, if access to "active" register triggers INTR. + sm_arb.request_mask |= mailbox.intr.priority_arbitration_bit; + // sm_arb_worker() evaluates this, extern Arbitrator raises Grant, + // vector of GRANted level is transfered with statemachine sm_intr + + // Atomically change state in a device's associates interrupt register. + // The Interupt Register is set immediately. No wait for INTR GRANT, + // INTR level may be blocked. + if (mailbox.intr.iopage_register_handle) + deviceregisters.registers[mailbox.intr.iopage_register_handle].value = + mailbox.intr.iopage_register_value; + mailbox.arm2pru_req = ARM2PRU_NONE; // done + // end of INTR is signaled to ARM with signal + break; + case ARM2PRU_INTR_CANCEL: + // cancels an INTR request. If already Granted, the GRANT is forwarded, + // and canceled by reaching a "SACK turnaround terminator" or "No SACK TIMEOUT" in the arbitrator. + sm_arb.request_mask &= ~ mailbox.intr.priority_arbitration_bit ; + // no completion event, could interfer with othe INTRs? + mailbox.arm2pru_req = ARM2PRU_NONE; // done + break ; + case ARM2PRU_INITPULSE: + if (!sm_init_state) + sm_init_state = (statemachine_state_func) &sm_init_start; + // INIT aborts DMA + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_POWERCYCLE: + sm_powercycle_state = (statemachine_state_func) &sm_powercycle_start; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_HALT: + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + __halt() ; // LA: trigger on timeout of REG_WRITE + break ; } - // now SACK held and BBSY set, slave state machine ended, since BBSY found inactive - - // debug pin reset by bus access - //DEBUG_PIN_SET(1) ; - sm_dma_state = (statemachine_state_func)&sm_dma_start; - while (sm_dma_state = sm_dma_state()) - //DEBUG_PIN_SET(1) ; - ;// execute dma master cycles - - // a dma cycle into a device register may trigger an interrupt - // do not delete that condition - if (mailbox.arm2pru_req == arm2pru_req_cached) - mailbox.arm2pru_req = ARM2PRU_NONE; // clear request - break; - case ARM2PRU_INTR: - // start one INTR cycle. May be raised in midst of slave cycle - // by ARM, if access to "active" register triggers INTR. - // no multiple levels simultaneously allowed, not parallel with DMA ! - sm_arb_state = (statemachine_state_func)sm_arb_start(mailbox.intr.priority_bit); - // wait while INTR is accepted. This may take long time, - // if system is at high processor priority (PSW register) - while (sm_arb_state && (sm_arb_state = sm_arb_state())) { - // sm_slave is most time critical, as it must keep track with MSYN/SSYN bus traffic. - // so give it more cpu cycles - sm_slave_state = (statemachine_state_func)&sm_slave_start; - while (sm_slave_state = sm_slave_state()) - ; - } - - // now SACK held and BBSY set, slave state machine ended, since BBSY found inactive - sm_intr_state = (statemachine_state_func)&sm_intr_start; - while (sm_intr_state = sm_intr_state()) - ; // execute intr cycle as bus master - mailbox.arm2pru_req = ARM2PRU_NONE; // clear request - break; - case ARM2PRU_INITPULSE: // generate a pulse on UNIBUS INIT - // only busmaster may assert INIT. violated here! - - // while INIT cycle is running, do slave cycles - sm_init_state = (statemachine_state_func)&sm_init_start; - while(sm_init_state=sm_init_state()) { - if (sm_slave_state) - sm_slave_state = sm_slave_state() ; - else // restart - sm_slave_state = (statemachine_state_func)&sm_slave_start; - } - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break; - case ARM2PRU_POWERCYCLE: // do ACLO/DCLO power cycle - // Runs for 4* POWERCYCLE_DELAY_MS millsecs, approx 1 sec. - // perform slave states in parallel, so emulated memory - // is existent for power fail trap and reboot - - // while power cycle is running, do slave cycles - sm_powercycle_state = (statemachine_state_func)&sm_powercycle_start; - while(sm_powercycle_state=sm_powercycle_state()) { - if (sm_slave_state) - sm_slave_state = sm_slave_state() ; - else // restart - sm_slave_state = (statemachine_state_func)&sm_slave_start; - } - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break; - - default: // ignore all other requestes while executing emulation - ; - } // switch - } // while (true) + } + } // never reached } + #pragma diag_pop diff --git a/10.01_base/2_src/pru1/pru1_pru_mailbox.c b/10.01_base/2_src/pru1/pru1_pru_mailbox.c index ac6bae6..fd5d8e6 100644 --- a/10.01_base/2_src/pru1/pru1_pru_mailbox.c +++ b/10.01_base/2_src/pru1/pru1_pru_mailbox.c @@ -1,43 +1,42 @@ /* pru1_pru_mailbox.c: datastructures common to PRU0 and PRU1 - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase + 12-nov-2018 JH entered beta phase - datastructures common to PRU0 and PRU1 - Compiled in both PR0 and PRU1 code - To be included into both PRU0 and PRU1 projects. - Section ".pru_pru_mailbox_sec" to be placed at the end of PRU0 internal RAM. - pos in PRU0 space: 0x1f00 - pos in PRU1 space: 0x3f00 - See linker control file! + datastructures common to PRU0 and PRU1 + Compiled in both PR0 and PRU1 code + To be included into both PRU0 and PRU1 projects. + Section ".pru_pru_mailbox_sec" to be placed at the end of PRU0 internal RAM. + pos in PRU0 space: 0x1f00 + pos in PRU1 space: 0x3f00 + See linker control file! */ #include #include "pru_pru_mailbox.h" #define _PRU_PRU_MAILBOX_C_ - // everything here is to be placed in the same section #pragma DATA_SECTION(pru_pru_mailbox,".pru_pru_mailbox_sec") -volatile pru_pru_mailbox_t pru_pru_mailbox ; +volatile pru_pru_mailbox_t pru_pru_mailbox; diff --git a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c index 4e70a50..deba29a 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c @@ -1,52 +1,52 @@ /* pru1_statemachine_arbitration.c: state machine for INTR/DMA arbitration - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase + 12-nov-2018 JH entered beta phase - Statemachine for execution of the Priority Arbitration protocol - NPR arbitration and BR interrupt arbitration + Statemachine for execution of the Priority Arbitration protocol + NPR arbitration and BR interrupt arbitration - UniBone is neither interrupt fielding processor nor abitrator. - It is a plain bus slave here. + PRU handles all 5 requests in parallel: + 4x INTR BR4-BR7 + 1x DMA NPR. + Several ARM device may raise the same BR|NPR level, ARM must serialize this to PRU. - This state machine can handle ONLY ONE request at a time. - ARM code threads must wait until a request is completed by PRU. - Problem: Multiple devices may trigger DMA and Interrupts independently! - Solution: these events must be serialized by an "priority arbitration scheduler" , - May be implemented by a event queue for each priority level, - containing refereneces to all devices requesting arbitration. + Flow: + 1. ARM sets a REQUEST by + filling the RQUEST struct and perhaps DMA data + doing AMR2PRO_PRIORITY_ARBITRATION_REQUEST, + 2. PRU sets BR4567|NPR lines according to open requests + 3. PRU monitors IN GRANT lines BG4567,NPG. + IN state of idle requests is forwarded to BG|NPG OUT liens, + to be processed by other UNIBUS cards. + BG*|NPG IN state line of active request cleares BR*|NPR line, + sets SACK, and starts INTR or DMA state machine. + 4. INTR or DMA sent a signal on compelte to PRU. + PRU may then start next request on same (completed) BR*|NPR level. - Or Todo: handle all 5 request/grants in parallel. - - 1. Start a request: set bit in _start() - 2. PRU executes statemachine - 3. on complete BBSY is acquires, UniBone is bus master - - - All references "PDP11BUS handbook 1979" - - At any time, CPU receives NPR it asserts NPG - - between CPU instructions: + All references "PDP11BUS handbook 1979" + - At any time, CPU receives NPR it asserts NPG + - between CPU instructions: if PRI < n and BRn is received, assert BGn else if PRI < 7 and BR7 is reived, assert BG7 else if PRI < 6 and BR6 is reived, assert BG6 @@ -54,7 +54,7 @@ else if PRI < 4 and BR4 is reived, assert BG4 - if PRU detectes a BGINn which it not requested, it passes it to BGOUTn + If PRU detectes a BGINn which it not requested, it passes it to BGOUTn "passing the grant" if PRU detects BGIN which was requests, it "blocks the GRANT" )sets SACK and transmit the INT (BG*) or becomes @@ -68,11 +68,18 @@ BBSY is set before SACK is released. SACK is relased imemdiatley after BBSY, enabling next arbitration in parallel to curretn data transfer "Only the device with helds SACk asserted can assert BBSY + + + Several arbitration "workers" which set request, monitor or generate GRANT signals + and allocate SACK. + Which worker to use depends on wether a physical PDP-11 CPU is Arbitrator, + the Arbitrator is implmented here (CPU emulation), + or DMA should be possible always + (even if some other CPU monitr is holding SACK (11/34). */ #define _PRU1_STATEMACHINE_ARBITRATION_C_ -#include #include #include "pru1_utils.h" @@ -80,145 +87,116 @@ #include "mailbox.h" #include "pru1_buslatches.h" -#include "pru1_statemachine_arbitration.h" +#include "pru1_statemachine_arbitration.h" statemachine_arbitration_t sm_arb; -// forwards ; -static statemachine_state_func sm_arb_state_1(void); -static statemachine_state_func sm_arb_state_2(void); -static statemachine_state_func sm_arb_state_3(void); - /********** NPR/NPG/SACK arbitrations **************/ -statemachine_state_func sm_arb_start(uint8_t priority_bit) { - sm_arb.priority_bit = priority_bit; // single priority bit for this arbitration process - return (statemachine_state_func)&sm_arb_state_1; + +// to be called on INIT signal: abort the abritration rpcoess +void sm_arb_reset() { + // cleanup: clear all REQUESTS and SACK + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK | BIT(5), 0); + sm_arb.request_mask = 0; + sm_arb.bbsy_wait_grant_mask = 0; } -// idle. call _start() -// execute in parallel with slave! -// pass BGIN[4-7],NPGIN to next device , if DMA engine idle -statemachine_state_func sm_arb_state_idle() { - uint8_t tmpval; - tmpval = buslatches_getbyte(0); - // forward all 5 GRANT IN inverted to GRANT OUT - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval) +/* sm_arb_workers_*() + If return !=0: we have SACK on the GRANt lines return in a bit mask + see PRIORITY_ARBITRATION_BIT_* + */ + +/* worker_none(): + * No arbitration protocol. Make DMA possible in every bus configuration: + * Ignores active SACK and/or BBSY from other bus masters. + * For diagnostics on hung CPU, active device or console processor holding SACK. + */ +uint8_t sm_arb_worker_none() { + // Unconditionally forward GRANT IN to GRANT OUT + uint8_t grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN + buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK, ~grant_mask) ; + + // ignore BR* INTR requests, only ack DMA. + if (sm_arb.request_mask & PRIORITY_ARBITRATION_BIT_NP) { + sm_arb.request_mask &= ~PRIORITY_ARBITRATION_BIT_NP; + return PRIORITY_ARBITRATION_BIT_NP; + } else + return 0; +} + +/* worker_client: + Issue request to extern Arbitrator (PDP-11 CPU). + Watch for GRANTs on the bus signal lines, then raise SACK. + Wait for current bus master to release bus => Wait for BBSY clear. + Then return GRANTed request. + "Wait for BBSY clear" may not be part of the arbitration protocol. + But it guarantees caller may now issue an DMA or INTR. + */ +uint8_t sm_arb_worker_client() { + uint8_t grant_mask; + + // Always update UNIBUS BR/NPR lines, are ORed with requests from other devices. + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK, sm_arb.request_mask) + ; + // read GRANT IN lines from CPU (Arbitrator). Only one at a time may be active + // Arbitrator asserts SACK is inactive + // latch[0]: BG signals are inverted, "get" is non-inverting: bit = active signal. + // "set" is inverting! + grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN + // forward un-requested GRANT IN to GRANT OUT for other cards on neighbor slots + buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK & ~sm_arb.request_mask, ~grant_mask) ; - return (statemachine_state_func)&sm_arb_state_idle; -} -// wait for GRANT idle -// Assert REQUEST, wait for GRANT, assert SACK, wait for NPG==0, set SACK=0 , -// execute in parallel with slave! -static statemachine_state_func sm_arb_state_1() { - uint8_t tmpval; - tmpval = buslatches_getbyte(0); - // forward all lines, until idle - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval) ; - // wait for GRANT idle, other cycle in progress? - if (tmpval & sm_arb.priority_bit) - return (statemachine_state_func)&sm_arb_state_1; // wait - // no need to wait for SACK: arbitrator responds only with a GRANT IN - buslatches_setbits(1, sm_arb.priority_bit, sm_arb.priority_bit); // REQUEST = latch1 - return (statemachine_state_func)&sm_arb_state_2; // wait for GRANT IN active -} + if (sm_arb.bbsy_wait_grant_mask == 0) { + // State 1: Wait For GRANT: + // process the requested grant for an open requests. + grant_mask &= sm_arb.request_mask; + if (grant_mask) { + // one of our requests was granted: + // set SACK + // AND simultaneously Clear granted requests BR*/NPR + // BIT(5): SACK mask and level + buslatches_setbits(1, (PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask) | BIT(5), + ~grant_mask | BIT(5)) + ; + /* + buslatches_setbits(1, BIT(5), BIT(5)); + // Arbitrator now clears GRANT IN + + // clear granted requests BR* / NPR on UNIBUS + // "~": disable BR for set BG + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask, ~grant_mask) + ; + */ - -// wait for BG*,NPG or INIT -// execute in parallel with slave! -static statemachine_state_func sm_arb_state_2() { - uint8_t tmpval; - - tmpval = buslatches_getbyte(0); - if (buslatches_getbyte(7) & BIT(3)) { // INIT stops transaction: latch[7], bit 3 - // cleanup: clear all 5 BR/NPR and SACK - buslatches_setbits(1, ARBITRATION_PRIORITY_MASK| BIT(5), 0); - - // forward all 5 GRANT IN inverted to GRANT OUT - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval) ; - - // Todo: signal INIT to ARM! - return NULL ; - } - // forward all other BG lines - // preceding arbitration must see BG removed by master on SACK - - if (tmpval & sm_arb.priority_bit) { - // got GRANT IN. Clear GRANT OUT, don't pass to next device - tmpval &= ~sm_arb.priority_bit ; - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval); // forward all without our GRANT - // set SACK - buslatches_setbits(1, BIT(5), BIT(5)); - return (statemachine_state_func)&sm_arb_state_3; + // clear granted requests internally + sm_arb.request_mask &= ~grant_mask; + // Arbitrator should remove GRANT now. Data section on Bus still BBSY + sm_arb.bbsy_wait_grant_mask = grant_mask; // next is State 2: wait for BBSY clear + } + return 0; // no GRANT for us, or wait for BBSY } else { - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval); // forward all - return (statemachine_state_func)&sm_arb_state_2 ; // wait + // wait for BBSY to clear + if (buslatches_getbyte(1) & BIT(6)) + return 0; // BBSY still set + grant_mask = sm_arb.bbsy_wait_grant_mask; + sm_arb.bbsy_wait_grant_mask = 0; // Next State is 1 + + return grant_mask; // signal what request we got granted. } + // UNIBUS DATA section is indepedent: MSYN, SSYN, BBSY may still be active. + // -> DMA and INTR statemachine must wait for BBSY. } -// GRANT received. wait for previous bus master to complete transaction, -// then become bus master -// Forwarding of other GRANTs not necessary ... arbitrator granted us. -static statemachine_state_func sm_arb_state_3() { - if (buslatches_getbyte(7) & BIT(3)) { // INIT stops transaction: latch[7], bit 3 - // cleanup: clear all REQUESTS and SACk - buslatches_setbits(1, ARBITRATION_PRIORITY_MASK| BIT(5), 0); - // Todo: signal INIT to ARM! - return NULL; - } - if (buslatches_getbyte(0) & sm_arb.priority_bit) // wait for GRANT IN to be deasserted - return (statemachine_state_func)&sm_arb_state_3; // wait - // wait until old bus master cleared BBSY - if (buslatches_getbyte(1) & BIT(6)) - return (statemachine_state_func)&sm_arb_state_3; // wait - // wait until SSYN deasserted by old slave - if (buslatches_getbyte(4) & BIT(5)) - return (statemachine_state_func)&sm_arb_state_3; // wait - // now become new bus master: Set BBSY, Clear REQUEST - // BBSY= bit 6 - buslatches_setbits(1, sm_arb.priority_bit | BIT(6), BIT(6)); - // SACK is cleared later in "data transfer" statemachines (DMA or INTR) - return NULL; // bus mastership acquired +/* "worker_master" + Act as Arbitrator. + Grant highest of requests, if SACK deasserted. + Execute UNIBUS priority algorithm: + - Grant DMA request, if present + - GRANT BR* in descending priority, when CPU execution level allows . + */ +uint8_t sm_arb_worker_master() { + return 0; } -#ifdef USED - -S1: -if SACK deasserted: - -set all Reqest lines in latch 1, which have bits set in mailbox.arb_request -(requests all in parallel - - wait for any GRANT - - // always: pass un-requestedgrants - read GRANT lines BG*IN, NPGIN - mask with my requests - write pattern to BG*OUT, NPGOUT - - S2: - get all 5 GRANT signals - get mask with highest asserted GRANT line ("active Grant") , which was requested in S1 - // always: pass un-requestedgrants to BG*OUT, NPGOUT - if grants for us: - set SACK - wait for active GRANT line going LOW - wait until BBSY==0 && SSYN==0 && active GRANT==0 free (long time!) - set BBSY - set SACK low - NO: SHOULD BE "BEFORE LAST DATA TRAMSFER BY CURRENT MASTER" - -> interaction with statemachine_master - setting SACK low early start arbitration early. - High priority interrupts while bus occpied by DMA (after ealry priority cycle) - are ignoreed then. - - set bit in mailbox in .arb_grant - - INIT: clear all requests, set INIT flag in mailbox! - - freeing BBSY: - on end of next DMA trasnfer, - OR after INTR transmission - -#endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h index 522b405..84f8cd5 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h @@ -1,44 +1,68 @@ /* pru1_statemachine_arbitration.h: state machine for INTR/DMA arbitration - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_ARBITRATION_H_ #define _PRU1_STATEMACHINE_ARBITRATION_H_ -#include "pru1_utils.h" // statemachine_state_func +#include + +// a priority-arbitration-worker returns a bit mask with the GRANT signal he recognized + +typedef uint8_t (*statemachine_arb_worker_func)(); typedef struct { - uint8_t priority_bit ; // single priority bit for this arbitration process - // one of ARBITRATION_PRIORITY_* + // There are 5 request/grant signals (BR4,5,6,7 and NPR). + // These are encoded as bitmask fitting the buslatch[0] or[1] + // BR/NPR lines = set of _PRIORITY_ARBITRATION_BIT_* + uint8_t request_mask; + + // sm_arb has 2 states: State 1 "Wait for GRANT" and State 2 "wait for BBSY" + // When arbitrator GRANts a request, we set SACK, GRAMT is cleared and we wait + // for BBSY clear. + // 0: not waitong for BBSY. + // != saves GRANTed reqiest and idnicates BBSY wait state + uint8_t bbsy_wait_grant_mask ; + + } statemachine_arbitration_t; +/* receives a grant_mask with 1 bit set and returns the index of that bit + when interpreted as intr odma request + BR4->0, BR5->1,BR6->2, BR7->3,NPR->4 + grant_mask as value from buslatch 0: BR4 at bit 0 + Undefined result if grant_mask empty or > 0x10 + */ +#define PRIORITY_ARBITRATION_INTR_BIT2IDX(grant_mask) \ + __lmbd((grant_mask), 1) +// "LMBD" = "Left Most Bit Detect" -#ifndef _PRU1_STATEMACHINE_ARBITRATION_C_ extern statemachine_arbitration_t sm_arb; -#endif -statemachine_state_func sm_arb_start(uint8_t priority_bit); -statemachine_state_func sm_arb_state_idle(void) ; +void sm_arb_reset(void); +uint8_t sm_arb_worker_none(void); +uint8_t sm_arb_worker_client(void); +uint8_t sm_arb_worker_master(void); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_dma.c b/10.01_base/2_src/pru1/pru1_statemachine_dma.c index 365c73b..c9af48d 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_dma.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_dma.c @@ -1,54 +1,54 @@ /* pru1_statemachine_dma.c: state machine for bus master DMA - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - Statemachines to execute multiple masterr DATO or DATI cycles. - All references "PDP11BUS handbook 1979" - Precondition: BBSY already asserted (arbitration got) + Statemachines to execute multiple masterr DATO or DATI cycles. + All references "PDP11BUS handbook 1979" + Precondition: BBSY already asserted (arbitration got) - Master reponds to INIT by stopping transactions. - new state + Master reponds to INIT by stopping transactions. + new state - Start: setup dma mailbox setup with - startaddr, wordcount, cycle, words[] - Then sm_dma_init() ; - sm_dma_state = DMA_STATE_RUNNING ; - while(sm_dma_state != DMA_STATE_READY) - sm_dma_service() ; - state is 0 for OK, or 2 for timeout error. - mailbox.dma.cur_addr is error location + Start: setup dma mailbox setup with + startaddr, wordcount, cycle, words[] + Then sm_dma_init() ; + sm_dma_state = DMA_STATE_RUNNING ; + while(sm_dma_state != DMA_STATE_READY) + sm_dma_service() ; + state is 0 for OK, or 2 for timeout error. + mailbox.dma.cur_addr is error location - Speed: (clpru 2.2, -O3: - Example: DATI, time SSYN- active -> (processing) -> MSYN inactive - a) 2 states, buslatch_set/get function calls, TIMEOUT_SET/REACHED(75) -> 700ns - b) 2 states, buslatch_set/get macro, TIMEOUT_SET/REACHED(75) -> 605ns - c) 2 states, no TIMEOUT (75 already met) -> 430ns - d) 1 marged state, no TIMEOUT ca. 350ns + Speed: (clpru 2.2, -O3: + Example: DATI, time SSYN- active -> (processing) -> MSYN inactive + a) 2 states, buslatch_set/get function calls, TIMEOUT_SET/REACHED(75) -> 700ns + b) 2 states, buslatch_set/get macro, TIMEOUT_SET/REACHED(75) -> 605ns + c) 2 states, no TIMEOUT (75 already met) -> 430ns + d) 1 marged state, no TIMEOUT ca. 350ns - ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! + ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! */ #define _PRU1_STATEMACHINE_DMA_C_ @@ -68,7 +68,7 @@ /* sometimes short timeout of 75 and 150ns are required * 75ns between state changes is not necessary, code runs longer * 150ns between state changes is necessary - * Overhead for extra state and TIEOUTSET/REACHED is 100ns + * Overhead for extra state and TIMEOUTSET/REACHED is 100ns */ statemachine_dma_t sm_dma; @@ -83,7 +83,7 @@ static statemachine_state_func sm_dma_state_99(void); // dma mailbox setup with // startaddr, wordcount, cycle, words[] ? // "cycle" must be UNIBUS_CONTROL_DATI or UNIBUS_CONTROL_DATO -// BBSY already set, SACK held asserted +// Wait for BBSY, SACK already held asserted statemachine_state_func sm_dma_start() { // assert BBSY: latch[1], bit 6 // buslatches_setbits(1, BIT(6), BIT(6)); @@ -92,10 +92,24 @@ statemachine_state_func sm_dma_start() { sm_dma.dataptr = (uint16_t *) mailbox.dma.words; // point to start of data buffer sm_dma.cur_wordsleft = mailbox.dma.wordcount; mailbox.dma.cur_status = DMA_STATE_RUNNING; + + // do not wait for BBSY here. This is part of Arbitration. + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY // next call to sm_dma.state() starts state machine - return (statemachine_state_func)&sm_dma_state_1 ; + return (statemachine_state_func) &sm_dma_state_1; } +/* +// wait for BBSY deasserted, then assert +static statemachine_state_func sm_dma_state_1() { + if (buslatches_getbyte(1) & BIT(6)) + return (statemachine_state_func) &sm_dma_state_1; // wait + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY + return (statemachine_state_func) &sm_dma_state_1; +} +*/ + + // place address and control bits onto bus, also data for DATO // If slave address is internal (= implemented by UniBone), // fast UNIBUS slave protocol is generated on the bus. @@ -107,12 +121,11 @@ static statemachine_state_func sm_dma_state_1() { // uint8_t page_table_entry; uint8_t b; bool internal; - - // should test SACK and BBSY ! + + // BBSY released if (mailbox.dma.cur_status != DMA_STATE_RUNNING || mailbox.dma.wordcount == 0) return NULL; // still stopped - if (sm_dma.cur_wordsleft == 1) { // deassert SACK, enable next arbitration cycle // deassert SACK before deassert BBSY @@ -176,17 +189,18 @@ static statemachine_state_func sm_dma_state_1() { buslatches_setbits(4, BIT(4), 0); // master deassert MSYN buslatches_setbyte(5, 0); // master removes data buslatches_setbyte(6, 0); - // perhaps PRU2ARM_INTERRUPT now active, - // assert SSYN after ARM completes "active" register logic and INIT - while (mailbox.events.eventmask) ; + // perhaps ARM issued ARM2PRU_INTR, request set in parallel state machine. + // Arbitrator will GRANT it after DMA ready (SACK deasserted). + // assert SSYN after ARM completes "active" register logic + // while (mailbox.events.event_deviceregister) ; buslatches_setbits(4, BIT(5), 0); // slave deassert SSYN - return (statemachine_state_func)&sm_dma_state_99; // next word + return (statemachine_state_func) &sm_dma_state_99; // next word } else { // DATO to external slave // wait for a slave SSYN timeout_set(TIMEOUT_DMA, MICROSECS(UNIBUS_TIMEOUT_PERIOD_US)); - return (statemachine_state_func)&sm_dma_state_21; // wait SSYN DATAO + return (statemachine_state_func) &sm_dma_state_21; // wait SSYN DATAO } } else { // DATI or DATIP @@ -222,17 +236,18 @@ static statemachine_state_func sm_dma_state_1() { buslatches_setbits(4, BIT(4), 0); // master deassert MSYN buslatches_setbyte(5, 0); // slave removes data buslatches_setbyte(6, 0); - // perhaps PRU2ARM_INTERRUPT now active, - // assert SSYN after ARM completes "active" register logic and INIT - while (mailbox.events.eventmask) ; + // perhaps ARM issued ARM2PRU_INTR, request set in parallel state machine. + // Arbitrator will GRANT it after DMA ready (SACK deasserted). + // assert SSYN after ARM completes "active" register logic + // while (mailbox.events.event_deviceregister) ; buslatches_setbits(4, BIT(5), 0); // slave deassert SSYN - return (statemachine_state_func)&sm_dma_state_99; // next word + return (statemachine_state_func) &sm_dma_state_99; // next word } else { // DATI to external slave // wait for a slave SSYN timeout_set(TIMEOUT_DMA, MICROSECS(UNIBUS_TIMEOUT_PERIOD_US)); - return (statemachine_state_func)&sm_dma_state_11; // wait SSYN DATI + return (statemachine_state_func) &sm_dma_state_11; // wait SSYN DATI } } } @@ -243,7 +258,7 @@ static statemachine_state_func sm_dma_state_11() { sm_dma.state_timeout = timeout_reached(TIMEOUT_DMA); // SSYN = latch[4], bit 5 if (!sm_dma.state_timeout && !(buslatches_getbyte(4) & BIT(5))) - return (statemachine_state_func)&sm_dma_state_11; // no SSYN yet: wait + return (statemachine_state_func) &sm_dma_state_11; // no SSYN yet: wait // SSYN set by slave (or timeout). read data __delay_cycles(NANOSECS(75) - 6); // assume 2*3 cycles for buslatches_getbyte @@ -258,7 +273,7 @@ static statemachine_state_func sm_dma_state_11() { buslatches_setbits(4, BIT(4), 0); // DATI: remove address,control, MSYN,SSYN from bus, 75ns after MSYN inactive __delay_cycles(NANOSECS(75) - 8); // assume 8 cycles for state change - return (statemachine_state_func)&sm_dma_state_99; + return (statemachine_state_func) &sm_dma_state_99; } // DATO to external slave: wait for SSYN or timeout @@ -266,7 +281,7 @@ static statemachine_state_func sm_dma_state_21() { sm_dma.state_timeout = timeout_reached(TIMEOUT_DMA); // SSYN timeout? // SSYN = latch[4], bit 5 if (!sm_dma.state_timeout && !(buslatches_getbyte(4) & BIT(5))) - return (statemachine_state_func)&sm_dma_state_21; // no SSYN yet: wait + return (statemachine_state_func) &sm_dma_state_21; // no SSYN yet: wait // SSYN set by slave (or timeout): negate MSYN, remove DATA from bus buslatches_setbits(4, BIT(4), 0); // deassert MSYN @@ -274,7 +289,7 @@ static statemachine_state_func sm_dma_state_21() { buslatches_setbyte(6, 0); // DATO: remove address,control, MSYN,SSYN from bus, 75ns after MSYN inactive __delay_cycles(NANOSECS(75) - 8); // assume 8 cycles for state change - return (statemachine_state_func)&sm_dma_state_99; + return (statemachine_state_func) &sm_dma_state_99; } // word is transfered, or timeout. @@ -306,7 +321,7 @@ static statemachine_state_func sm_dma_state_99() { if (final_dma_state == DMA_STATE_RUNNING) { // dataptr and words_left already incremented mailbox.dma.cur_addr += 2; // signal progress to ARM - return (statemachine_state_func)&sm_dma_state_1; // reloop + return (statemachine_state_func) &sm_dma_state_1; // reloop } else { // remove addr and control from bus buslatches_setbyte(2, 0); @@ -314,9 +329,19 @@ static statemachine_state_func sm_dma_state_99() { buslatches_setbits(4, 0x3f, 0); // remove BBSY: latch[1], bit 6 buslatches_setbits(1, BIT(6), 0); - // SACK already de-asserted at wordcount==1 - timeout_cleanup(TIMEOUT_DMA) ; + // SACK already de-asserted at wordcount==1 + timeout_cleanup(TIMEOUT_DMA); mailbox.dma.cur_status = final_dma_state; // signal to ARM + + timeout_cleanup(TIMEOUT_DMA); + + // signal to ARM + mailbox.events.event_dma = 1; + // ARM is clearing this, before requesting new DMA. + // no concurrent ARP+PRU access + PRU2ARM_INTERRUPT + ; + return NULL; // now stopped } } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_dma.h b/10.01_base/2_src/pru1/pru1_statemachine_dma.h index cf266b4..4a1a432 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_dma.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_dma.h @@ -1,51 +1,46 @@ /* pru1_statemachine_dma.h: state machine for bus master DMA - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_DMA_H_ #define _PRU1_STATEMACHINE_DMA_H_ - #include "pru1_utils.h" // statemachine_state_func - // Transfers a block of worst as data cycles typedef struct { uint8_t state_timeout; // timeout occured? - uint16_t *dataptr ; // points to current word in mailbox.words[] ; + uint16_t *dataptr; // points to current word in mailbox.words[] ; uint16_t cur_wordsleft; // # of words left to transfer } statemachine_dma_t; - - #ifndef _PRU1_STATEMACHINE_DMA_C_ extern statemachine_dma_t sm_dma; #endif - -statemachine_state_func sm_dma_start(void) ; +statemachine_state_func sm_dma_start(void); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_init.c b/10.01_base/2_src/pru1/pru1_statemachine_init.c index a59a87d..76ab6ff 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_init.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_init.c @@ -1,31 +1,31 @@ /* pru1_statemachine_init.c: state machine for pulse on INIT - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - Statemachine for a pulse on UNIBUS INIT - ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! + Statemachine for a pulse on UNIBUS INIT + ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! */ #define _PRU1_STATEMACHINE_INIT_C_ @@ -39,7 +39,7 @@ #include "pru1_buslatches.h" #include "pru1_statemachine_init.h" - +#include "pru1_statemachine_arbitration.h" /*** detection of changes in INIT,DCLO,ACLO ***/ @@ -47,41 +47,54 @@ // history initialized (among others) by powercycle // Assume this events come so slow, no one gets raised until // prev event processed. -void do_event_initializationsignals() { - uint8_t tmp = buslatches_getbyte(7) & 0x38 ; - if (tmp != mailbox.events.initialization_signals_cur) { - // save old state, so ARM can detect what changed - mailbox.events.initialization_signals_prev = mailbox.events.initialization_signals_cur ; - mailbox.events.initialization_signals_cur = tmp ; - mailbox.events.eventmask |= EVENT_INITIALIZATIONSIGNALS ; - PRU2ARM_INTERRUPT ; - } +void do_event_initializationsignals() { + uint8_t mb_cur = mailbox.events.initialization_signals_cur; // as saved + uint8_t bus_cur = buslatches_getbyte(7) & 0x38; // now sampled + + if (bus_cur & INITIALIZATIONSIGNAL_INIT) + sm_arb.request_mask = 0 ; // INIT clears all PRIORITY request signals + // SACK cleared later on end of INTR/DMA transaction + + if (bus_cur != mb_cur) { + // save old state, so ARM can detect what changed + mailbox.events.initialization_signals_prev = mb_cur; + mailbox.events.initialization_signals_cur = bus_cur; + // trigger the correct event: power and/or INIT + if ((mb_cur ^ bus_cur) & (INITIALIZATIONSIGNAL_DCLO | INITIALIZATIONSIGNAL_ACLO)) + // AC_LO or DC_LO changed + mailbox.events.event_power = 1; + if ((mb_cur ^ bus_cur) & INITIALIZATIONSIGNAL_INIT) + // INIT changed + mailbox.events.event_init = 1; + PRU2ARM_INTERRUPT + ; } -// No event queue: INIT event may overide register access ... thats OK: -// INIT reset all register states - - +} // forwards static statemachine_state_func sm_init_state_1(void); // setup statemachine_state_func sm_init_start() { - timeout_set(TIMEOUT_INIT, MILLISECS(INITPULSE_DELAY_MS)) - ; + timeout_set(TIMEOUT_INIT, MILLISECS(INITPULSE_DELAY_MS)); // INIT: latch[7], bit 3 buslatches_setbits(7, BIT(3), BIT(3)); // assert INIT - mailbox.events.initialization_signals_prev &= ~INITIALIZATIONSIGNAL_INIT ; // force INIT event - do_event_initializationsignals() ; - return (statemachine_state_func)&sm_init_state_1; + mailbox.events.initialization_signals_prev &= ~INITIALIZATIONSIGNAL_INIT; // force INIT event + return (statemachine_state_func) &sm_init_state_1; } - static statemachine_state_func sm_init_state_1() { if (!timeout_reached(TIMEOUT_INIT)) - return (statemachine_state_func)&sm_init_state_1; // wait + return (statemachine_state_func) &sm_init_state_1; // wait buslatches_setbits(7, BIT(3), 0); // deassert INIT - do_event_initializationsignals() ; - timeout_cleanup(TIMEOUT_INIT) ; - return NULL ; // ready + timeout_cleanup(TIMEOUT_INIT); + return NULL; // ready +} + +// terminate INIT +// To be called from Powercycle? +void sm_init_reset() { + buslatches_setbits(7, BIT(3), 0); // deassert INIT + do_event_initializationsignals(); + timeout_cleanup(TIMEOUT_INIT); } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_init.h b/10.01_base/2_src/pru1/pru1_statemachine_init.h index 707bf20..bbfd850 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_init.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_init.h @@ -1,29 +1,29 @@ /* pru1_statemachine_init.h: state machine for pulse on INIT - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_INIT_H_ #define _PRU1_STATEMACHINE_INIT_H_ @@ -33,13 +33,13 @@ #define INITPULSE_DELAY_MS 250 // length of INIT pulse - #ifndef _PRU1_STATEMACHINE_INIT_C_ -extern uint8_t prev_initialization_signals ; +extern uint8_t prev_initialization_signals; #endif -void do_event_initializationsignals(void) ; +void do_event_initializationsignals(void); +void sm_init_reset(void); statemachine_state_func sm_init_start(); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_intr.c b/10.01_base/2_src/pru1/pru1_statemachine_intr.c index 59d8960..ba169cd 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_intr.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_intr.c @@ -1,32 +1,32 @@ /* pru1_statemachine_intr.c: state machine to transfer an interrupt vector - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - State machines to transfer an interrupt vector. - All references "PDP11BUS handbook 1979" - Precondition: BBSY already asserted (arbitration got) + State machines to transfer an interrupt vector. + All references "PDP11BUS handbook 1979" + Precondition: BBSY already asserted (arbitration got) */ #define _PRU1_STATEMACHINE_INTR_C_ @@ -42,24 +42,27 @@ //#include "pru1_statemachine_arbitration.h" #include "pru1_statemachine_intr.h" +// states +statemachine_intr_t sm_intr; + // forwards static statemachine_state_func sm_intr_state_1(void); static statemachine_state_func sm_intr_state_2(void); -// BBSY already set, SACK held asserted +// Wait for BBSY deasserted, then assert, SACK already held asserted statemachine_state_func sm_intr_start() { - // BBSY already asserted. : latch[1], bit 6 - // buslatches_setbits(1, BIT(6), BIT(6)); - return (statemachine_state_func)&sm_intr_state_1; - // next call to sm_intr.state() starts state machine + // Do not wait for BBSY here, this is part of Arbitration + // if (buslatches_getbyte(1) & BIT(6)) + // return (statemachine_state_func) &sm_intr_start; // wait + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY + return (statemachine_state_func) &sm_intr_state_1; } // place vector onto data, then set INTR static statemachine_state_func sm_intr_state_1() { - uint16_t vector = mailbox.intr.vector; - buslatches_setbyte(5, vector & 0xff); // DATA[0..7] = latch[5] - buslatches_setbyte(6, vector >> 8); // DATA[8..15] = latch[6] + buslatches_setbyte(5, sm_intr.vector & 0xff); // DATA[0..7] = latch[5] + buslatches_setbyte(6, sm_intr.vector >> 8); // DATA[8..15] = latch[6] // set INTR buslatches_setbits(7, BIT(0), BIT(0)); // INTR = latch 7, bit 0 @@ -70,22 +73,40 @@ static statemachine_state_func sm_intr_state_1() { buslatches_setbits(1, BIT(5), 0); // SACK = latch[1], bit 5 // wait for processor to accept vector (no timeout?) - return (statemachine_state_func)&sm_intr_state_2; + return (statemachine_state_func) &sm_intr_state_2; } // wait for SSYN static statemachine_state_func sm_intr_state_2() { if (!(buslatches_getbyte(4) & BIT(5))) - return (statemachine_state_func)&sm_intr_state_2; // wait + return (statemachine_state_func) &sm_intr_state_2; // wait // received SSYN - // remove vector, then remove INTR + // Complete and signal this INTR transaction only after ARM has processed the previous event. + // INTR may come faster than ARM Linux can process, + // especially if Arbitrator grants INTRs of multiple levels almost simultaneaously in parallel. + if (mailbox.events.event_intr) + return (statemachine_state_func) &sm_intr_state_2; // wait + + // remove vector buslatches_setbyte(5, 0); // DATA[0..7] = latch[5] buslatches_setbyte(6, 0); // DATA[8..15] = latch[6] + + // deassert INTR. Interrupt fielding processor then removes SSYN buslatches_setbits(7, BIT(0), 0); // INTR = latch 7, bit 0 + // deassert BBSY buslatches_setbits(1, BIT(6), 0); - // SACK already removed + // SACK already removed + + // signal to ARM which INTR was completed + // change mailbox only after ARM has ack'ed mailbox.events.event_intr + mailbox.events.event_intr_level_index = sm_intr.level_index; + mailbox.events.event_intr = 1; + // ARM is clearing this, before requesting new interrupt of same level + // so no concurrent ARP+PRU access + PRU2ARM_INTERRUPT + ; return NULL; // ready // master still drives SSYN diff --git a/10.01_base/2_src/pru1/pru1_statemachine_intr.h b/10.01_base/2_src/pru1/pru1_statemachine_intr.h index a8c7e70..3361621 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_intr.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_intr.h @@ -1,36 +1,40 @@ /* pru1_statemachine_intr.h: state machine to transfer an interrupt vector - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_INTR_H_ #define _PRU1_STATEMACHINE_INTR_H_ #include "pru1_utils.h" // statemachine_state_func -// Transfers a block of worst as data cycles +typedef struct { + uint16_t vector; // interrupt vector to transfer + uint8_t level_index; // 0..3 = BR..BR7. to be returned to ARM on complete +} statemachine_intr_t; +extern statemachine_intr_t sm_intr; statemachine_state_func sm_intr_start(void); diff --git a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c index 3b21eab..948eee4 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c @@ -1,28 +1,28 @@ /* pru1_statemachine_powercycle.c: state machine to generate an ACLO/DCLO pseudo power cycle - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase Statemachine for execution of an ACLO/DCLO pseudo power cycle. @@ -62,52 +62,48 @@ static statemachine_state_func sm_powercycle_state_4(void); // setup with statemachine_state_func sm_powercycle_start() { - return (statemachine_state_func)&sm_powercycle_state_1; + return (statemachine_state_func) &sm_powercycle_state_1; // next call to sm_slave.state() starts state machine } - // "Line power shutdown": assert ACLO, then wait static statemachine_state_func sm_powercycle_state_1() { buslatches_setbits(7, BIT(4), BIT(4)); // ACLO asserted - timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)) ; // wait for DC power shutdown + timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // wait for DC power shutdown // DEBUG_OUT(0x01) ; - do_event_initializationsignals() ; // DEBUG_OUT(0x02) ; - return (statemachine_state_func)&sm_powercycle_state_2; + return (statemachine_state_func) &sm_powercycle_state_2; } // "Power supply switched off": assert DCLO, then wait static statemachine_state_func sm_powercycle_state_2() { if (!timeout_reached(TIMEOUT_POWERCYCLE)) - return (statemachine_state_func)&sm_powercycle_state_2; // wait + return (statemachine_state_func) &sm_powercycle_state_2; // wait buslatches_setbits(7, BIT(5), BIT(5)); // DCLO asserted - timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)) - ; // system powered off - // DEBUG_OUT(0x03) ; - do_event_initializationsignals() ; - // DEBUG_OUT(0x04) ; - return (statemachine_state_func)&sm_powercycle_state_3; + timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // system powered off + // DEBUG_OUT(0x03) ; + // DEBUG_OUT(0x04) ; + // make sure we don't "come up from power" with INIT already set. + sm_init_reset(); + + return (statemachine_state_func) &sm_powercycle_state_3; } // "Line power back again": deassert ACLO, then wait static statemachine_state_func sm_powercycle_state_3() { if (!timeout_reached(TIMEOUT_POWERCYCLE)) - return (statemachine_state_func)&sm_powercycle_state_3; // wait + return (statemachine_state_func) &sm_powercycle_state_3; // wait buslatches_setbits(7, BIT(4), 0); // ACLO deasserted - timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)) - ; // "power supply stabilizing" - do_event_initializationsignals() ; - return (statemachine_state_func)&sm_powercycle_state_4; + timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // "power supply stabilizing" + return (statemachine_state_func) &sm_powercycle_state_4; } // "Logic power stabilized": deassert DCLO, ready static statemachine_state_func sm_powercycle_state_4() { if (!timeout_reached(TIMEOUT_POWERCYCLE)) - return (statemachine_state_func)&sm_powercycle_state_4; - buslatches_setbits(7, BIT(5), 0); // DCLO deasserted - do_event_initializationsignals() ; - timeout_cleanup(TIMEOUT_POWERCYCLE) ; + return (statemachine_state_func) &sm_powercycle_state_4; + buslatches_setbits(7, BIT(5), 0); // DCLO deasserted + timeout_cleanup(TIMEOUT_POWERCYCLE); return NULL; } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h index fdba431..bc96363 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h @@ -1,29 +1,29 @@ /* pru1_statemachine_powercycle.h: state machine to generate an ACLO/DCLO pseudo power cycle - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_POWERCYCLE_H_ #define _PRU1_STATEMACHINE_POWERCYCLE_H_ @@ -32,7 +32,6 @@ #define POWERCYCLE_DELAY_MS 100 // wait period in millsecs between ACLO/DCLO transitions - statemachine_state_func sm_powercycle_start(); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_slave.c b/10.01_base/2_src/pru1/pru1_statemachine_slave.c index f277cd5..d822a13 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_slave.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_slave.c @@ -1,37 +1,37 @@ /* pru1_statemachine_slave.c: state machine for execution of slave DATO* or DATI* cycles - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - Statemachine for execution of slave DATO* or DATI* cycles. - All references "PDP11BUS handbook 1979" + Statemachine for execution of slave DATO* or DATI* cycles. + All references "PDP11BUS handbook 1979" - Slave reponds not to INIT on this level, master must stop bus transactions. + Slave reponds not to INIT on this level, master must stop bus transactions. - - Slave waits for MSYN L->H - - slaves fetches address and control lines - - address is evaluated, ggf mem access + - Slave waits for MSYN L->H + - slaves fetches address and control lines + - address is evaluated, ggf mem access */ #define _PRU1_STATEMACHINE_SLAVE_C_ @@ -54,7 +54,6 @@ static statemachine_state_func sm_slave_state_10(void); static statemachine_state_func sm_slave_state_20(void); static statemachine_state_func sm_slave_state_99(void); - // check for MSYN active statemachine_state_func sm_slave_start() { uint8_t latch2val, latch3val, latch4val; @@ -110,14 +109,13 @@ statemachine_state_func sm_slave_start() { buslatches_setbyte(5, data & 0xff); // DATA[8..15] = latch[6] buslatches_setbyte(6, data >> 8); - //DEBUG_PIN_PULSE ; // trigger scope/LA. auto cleared on next reg_sel // set SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), BIT(5)); - return (statemachine_state_func)&sm_slave_state_20; + return (statemachine_state_func) &sm_slave_state_20; // perhaps PRU2ARM_INTERRUPT now active } else // no address match: wait for MSYN to go inactive - return (statemachine_state_func)&sm_slave_state_99; + return (statemachine_state_func) &sm_slave_state_99; case UNIBUS_CONTROL_DATO: // fetch data in any case // DATA[0..7] = latch[5] @@ -125,16 +123,15 @@ statemachine_state_func sm_slave_start() { // DATA[8..15] = latch[6] w |= (uint16_t) buslatches_getbyte(6) << 8; if (iopageregisters_write_w(addr, w)) { - //DEBUG_PIN_PULSE ; // trigger scope/LA. auto cleared on next reg_sel // SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), BIT(5)); // wait for MSYN to go inactive, then SSYN inactive - return (statemachine_state_func)&sm_slave_state_10; + return (statemachine_state_func) &sm_slave_state_10; // perhaps PRU2ARM_INTERRUPT now active } else // no address match: wait for MSYN to go inactive - return (statemachine_state_func)&sm_slave_state_99; + return (statemachine_state_func) &sm_slave_state_99; case UNIBUS_CONTROL_DATOB: // A00 = 1, odd address: get upper byte // A00 = 0: even address, get lower byte @@ -150,13 +147,13 @@ statemachine_state_func sm_slave_start() { // SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), BIT(5)); // wait for MSYN to go inactive, then SSYN inactive - return (statemachine_state_func)&sm_slave_state_10; + return (statemachine_state_func) &sm_slave_state_10; // perhaps PRU2ARM_INTERRUPT now active } else // no address match: wait for MSYN to go inactive - return (statemachine_state_func)&sm_slave_state_99; + return (statemachine_state_func) &sm_slave_state_99; } - return NULL ; // not reached + return NULL; // not reached } // End DATO: wait for MSYN to go inactive, then SSYN inactive @@ -164,11 +161,13 @@ statemachine_state_func sm_slave_start() { static statemachine_state_func sm_slave_state_10() { // MSYN = latch[4], bit 4 if (buslatches_getbyte(4) & BIT(4)) - return (statemachine_state_func)&sm_slave_state_10; // wait, MSYN still active - if (mailbox.events.eventmask) - return (statemachine_state_func)&sm_slave_state_10; // wait, long SSYN delay until ARM acknowledges all events + return (statemachine_state_func) &sm_slave_state_10; // wait, MSYN still active + if (mailbox.events.event_deviceregister) + // unibusadapter.worker() did not yet run on_after_register_access() + // => wait, long SSYN delay until ARM acknowledges event + return (statemachine_state_func) &sm_slave_state_10; // if ARM was triggered by event and changed the device state, - // now an Interrupt arbitration may be pending! + // now an Interrupt arbitration may be pending. // clear SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), 0); @@ -181,11 +180,13 @@ static statemachine_state_func sm_slave_state_10() { static statemachine_state_func sm_slave_state_20() { // MSYN = latch[4], bit 4 if (buslatches_getbyte(4) & BIT(4)) - return (statemachine_state_func)&sm_slave_state_20; // wait, MSYN still active - if (mailbox.events.eventmask) - return (statemachine_state_func)&sm_slave_state_20; // wait, long SSYN delay until ARM acknowledges event + return (statemachine_state_func) &sm_slave_state_20; // wait, MSYN still active + if (mailbox.events.event_deviceregister) + // unibusadapter.worker() did not yet run on_after_register_access() + // => wait, long SSYN delay until ARM acknowledges event + return (statemachine_state_func) &sm_slave_state_20; // if ARM was triggered by event and changed the device state, - // now an Interrupt arbitration may be pending! + // now an Interrupt arbitration may be pending. // clear first data, then SSYN // DATA[0..7] = latch[5] @@ -201,7 +202,7 @@ static statemachine_state_func sm_slave_state_20() { static statemachine_state_func sm_slave_state_99() { // MSYN = latch[4], bit 4 if (buslatches_getbyte(4) & BIT(4)) - return (statemachine_state_func)&sm_slave_state_99; // wait, MSYN still active + return (statemachine_state_func) &sm_slave_state_99; // wait, MSYN still active return NULL; // ready } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_slave.h b/10.01_base/2_src/pru1/pru1_statemachine_slave.h index 2f1a1e8..9e9bfd6 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_slave.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_slave.h @@ -1,29 +1,29 @@ /* pru1_statemachine_slave.h: state machine for execution of slave DATO* or DATI* cycles - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 29-jun-2019 JH rework: state returns ptr to next state func - 12-nov-2018 JH entered beta phase -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_SLAVE_H_ #define _PRU1_STATEMACHINE_SLAVE_H_ diff --git a/10.01_base/2_src/pru1/pru1_timeouts.c b/10.01_base/2_src/pru1/pru1_timeouts.c index 9236fb1..b89371a 100644 --- a/10.01_base/2_src/pru1/pru1_timeouts.c +++ b/10.01_base/2_src/pru1/pru1_timeouts.c @@ -1,45 +1,45 @@ /* pru1_timeouts.c: timeout conditions - Copyright (c) 2019, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2019, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 3-jul-2019 JH begin edit + 3-jul-2019 JH begin edit - Several timers are needed, but PRU hs only one - global cycle counter CYCLEOUNT. It is 32 bit and runs at 200 MHz, - so rollaround every 21 seconds. + Several timers are needed, but PRU hs only one + global cycle counter CYCLEOUNT. It is 32 bit and runs at 200 MHz, + so rollaround every 21 seconds. - Usage: - - if no timer is running, the first "timeout" reuqest clears CYCLEOUNT - - for each timer, the "timeout" cycle count is set - - tiemr msut be polled for timeout by user. - - a timer is considered "timed-out", if its timeout is 0. - - a global variable regsiteres the active running timeouts. - - a running timeout MUST be canceled, or polled until "timeout_rechaed" !! + Usage: + - if no timer is running, the first "timeout" reuqest clears CYCLEOUNT + - for each timer, the "timeout" cycle count is set + - tiemr msut be polled for timeout by user. + - a timer is considered "timed-out", if its timeout is 0. + - a global variable regsiteres the active running timeouts. + - a running timeout MUST be canceled, or polled until "timeout_rechaed" !! - The PRU CYCLECOUNT may not be reset if one timeout is active. - So the total run time of all parallel running timeous must not exceed 21 seconds - At least every 21 seconds all timers must be expired. + The PRU CYCLECOUNT may not be reset if one timeout is active. + So the total run time of all parallel running timeous must not exceed 21 seconds + At least every 21 seconds all timers must be expired. -*/ + */ #include #include @@ -47,46 +47,43 @@ #include "pru1_timeouts.h" // own // count running timers. -static uint8_t timeouts_active = 0 ; +static uint8_t timeouts_active = 0; // cycle end count for each active timeoput. -uint32_t timeout_target_cycles[TIMEOUT_COUNT] ; +uint32_t timeout_target_cycles[TIMEOUT_COUNT]; // all functions receive a pointer to one of these array members - - #define TIMEOUT_INTERNAL_CYCLES 24 void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles) { // stop timeout, if already running if (*target_cycles_var > 0) { - *target_cycles_var = 0 ; - timeouts_active-- ; // was inactive + *target_cycles_var = 0; + timeouts_active--; // was inactive } if (timeouts_active == 0) { - // must see this regulary, else count logic damaged! - // DEBUG_PIN_SET(1) ; // first timeout: clear and restart counter PRU1_CTRL.CTRL_bit.CTR_EN = 0; - PRU1_CTRL.CYCLE = 0 ; + PRU1_CTRL.CYCLE = 0; } /* 4 cycle used in TIMEOUT_REACHED */ if (delta_cycles < TIMEOUT_INTERNAL_CYCLES) - delta_cycles = 0 ; - else delta_cycles -= TIMEOUT_INTERNAL_CYCLES ; - *target_cycles_var = PRU1_CTRL.CYCLE + delta_cycles ; + delta_cycles = 0; + else + delta_cycles -= TIMEOUT_INTERNAL_CYCLES; + *target_cycles_var = PRU1_CTRL.CYCLE + delta_cycles; PRU1_CTRL.CTRL_bit.CTR_EN = 1; - timeouts_active++ ; // now one more active + timeouts_active++; // now one more active } // msut be called, if timeout polled anymore for "timeout-reached() void timeout_cleanup(uint32_t *target_cycles_var) { if (*target_cycles_var > 0) { - *target_cycles_var = 0 ; - timeouts_active-- ; // was inactive + *target_cycles_var = 0; + timeouts_active--; // was inactive } } @@ -96,22 +93,19 @@ void timeout_cleanup(uint32_t *target_cycles_var) { bool timeout_reached(uint32_t *target_cycles_var) { // fast path: assume timeout_reached() is called // because timeout is active - if ( PRU1_CTRL.CYCLE < *target_cycles_var) - return false ; + if (PRU1_CTRL.CYCLE < *target_cycles_var) + return false; else if (*target_cycles_var == 0) - return true ; // already inactive - else { + return true; // already inactive + else { // switched from "running" to "timeout reached" - *target_cycles_var = 0 ; - timeouts_active-- ; - return true ; + *target_cycles_var = 0; + timeouts_active--; + return true; } } - - - void timeout_init(void) { - timeouts_active = 0 ; - memset(timeout_target_cycles, 0, sizeof(uint32_t) * TIMEOUT_COUNT) ; + timeouts_active = 0; + memset(timeout_target_cycles, 0, sizeof(uint32_t) * TIMEOUT_COUNT); } diff --git a/10.01_base/2_src/pru1/pru1_timeouts.h b/10.01_base/2_src/pru1/pru1_timeouts.h index c41f868..717a533 100644 --- a/10.01_base/2_src/pru1/pru1_timeouts.h +++ b/10.01_base/2_src/pru1/pru1_timeouts.h @@ -1,34 +1,33 @@ /* pru1_timeouts.h: timeout conditions - Copyright (c) 2019, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2019, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 3-jul-2019 JH begin edit -*/ + 3-jul-2019 JH begin edit + */ #ifndef _PRU1_TIMEOUTS_H_ #define _PRU1_TIMEOUTS_H_ #include #include - // predefined timeouts #define TIMEOUT_COUNT 3 @@ -37,16 +36,15 @@ #define TIMEOUT_INIT (&timeout_target_cycles[1]) #define TIMEOUT_POWERCYCLE (&timeout_target_cycles[2]) - // cycle end count for each active timeoput. -extern uint32_t timeout_target_cycles[TIMEOUT_COUNT] ; +extern uint32_t timeout_target_cycles[TIMEOUT_COUNT]; // call all functions mit timeout_func(TIMEOUT_*,..) // This allows the compiler to optimize the timeout_target_cycles[idx] expr -void timeout_init(void) ; -void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles) ; -bool timeout_reached(uint32_t *target_cycles_var) ; -void timeout_cleanup(uint32_t *target_cycles_var) ; +void timeout_init(void); +void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles); +bool timeout_reached(uint32_t *target_cycles_var); +void timeout_cleanup(uint32_t *target_cycles_var); #endif diff --git a/10.01_base/2_src/pru1/pru1_utils.c b/10.01_base/2_src/pru1/pru1_utils.c index 27249e1..edd9f70 100644 --- a/10.01_base/2_src/pru1/pru1_utils.c +++ b/10.01_base/2_src/pru1/pru1_utils.c @@ -1,38 +1,32 @@ /* pru1_utils.c: misc. utilities - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #define _PRU1_UTILS_C_ - #include - #include "pru1_utils.h" - -uint32_t timeout_target ; - - diff --git a/10.01_base/2_src/pru1/pru1_utils.h b/10.01_base/2_src/pru1/pru1_utils.h index 237bb10..a60d0f7 100644 --- a/10.01_base/2_src/pru1/pru1_utils.h +++ b/10.01_base/2_src/pru1/pru1_utils.h @@ -1,32 +1,31 @@ /* pru1_utils.h: misc. utilities - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 12-nov-2018 JH entered beta phase -*/ + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_UTILS_H_ #define _PRU1_UTILS_H_ - #include #include @@ -36,19 +35,15 @@ volatile register uint32_t __R30; volatile register uint32_t __R31; - // execution of a state. return : next state; or NULL if statemachine stopped // return type is void *, but should be statemachine_state_func_ptr recursively // typedef statemachine_state_func * (*statemachine_state_func)(void); // Not possible?! So return void * and cast to void *(func(void) on use typedef void * (*statemachine_state_func)(void); - - #define MILLION 1000000L #define BILLION (1000L * MILLION) - // SSYN timeout after 20 milliseconds #define UNIBUS_TIMEOUT_PERIOD_US 20 @@ -68,18 +63,18 @@ typedef void * (*statemachine_state_func)(void); * Translated with "clpru 2.2, -O3" * * Test setup: -while (1) { - __R30 |= (1 << 8); - TIMEOUT_SET(NANOSECS(1000)) ; // 1 usec / level - while (!TIMEOUT_REACHED) ; - __R30 &= ~(1 << 8); - TIMEOUT_SET(NANOSECS(1000)) ; - while (!TIMEOUT_REACHED) ; -} -* -* Enhancement: save address of CYLCE in cosntant btabel C28, see -http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-prus-part-1/449 -* ALTERNATIVE 1: + while (1) { + __R30 |= (1 << 8); + TIMEOUT_SET(NANOSECS(1000)) ; // 1 usec / level + while (!TIMEOUT_REACHED) ; + __R30 &= ~(1 << 8); + TIMEOUT_SET(NANOSECS(1000)) ; + while (!TIMEOUT_REACHED) ; + } + * + * Enhancement: save address of CYLCE in cosntant btabel C28, see + http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-prus-part-1/449 + * ALTERNATIVE 1: * The Industrial Ethernet module has a general purpose time and compare registers: * 32 bit Counter is permanetly running cyclic through overlfow * TIMEOUT_SET: compare0 = counter + delay, clear compare0 flag @@ -91,24 +86,17 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru * dann nur compare0 nutzbar * counter overflow auf 2^32 5ns = 21 sek -* ALTERNATIVE 2: -* PRU0 decrements a mailbox value until it is 0. -* introduces delay in PRU0 outputs! + * ALTERNATIVE 2: + * PRU0 decrements a mailbox value until it is 0. + * introduces delay in PRU0 outputs! */ // set PRU1_12 to 0 or 1 -#define DEBUG_PIN_SET(val) ( (val) ? (__R30 |= (1 << 12) ) : (__R30 &= ~(1<< 12)) ) +#define PRU_DEBUG_PIN(val) ( (val) ? (__R30 |= (1 << 12) ) : (__R30 &= ~(1<< 12)) ) -// 20ns pulse an PRU1_12 -#define DEBUG_PIN_PULSE do { \ - __R30 |= (1 << 12) ; \ - __delay_cycles(4-1) ; \ - __R30 &= ~(1 << 12) ; \ - } while(0) - - -#define DEBUG_PIN_PULSE_100NS do { \ +// 100ns pulse an PRU1_12 +#define PRU_DEBUG_PIN_PULSE_100NS do { \ __R30 |= (1 << 12) ; \ __delay_cycles(18) ; \ __R30 &= ~(1 << 12) ; \ @@ -124,8 +112,6 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru } while(0) #endif - - // To signal the host that we're done, we set bit 5 in our R31 // simultaneously with putting the number of the signal we want // into R31 bits 0-3. See 5.2.2.2 in AM335x PRU-ICSS Reference Guide. @@ -137,13 +123,6 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru #define PRU2ARM_INTERRUPT do { \ /* is Interrupt "level" or "edge"??? */ \ __R31 = PRU2ARM_INTERRUPT_PRU0_R31_VEC_VALID |PRU2ARM_INTERRUPT_SIGNUM ; /* 35 */ \ -DEBUG_PIN_PULSE ; \ } while(0) - -#ifndef _PRU1_UTILS_C_ -extern uint32_t timeout_target ; -#endif - - #endif diff --git a/10.01_base/3_test/intrtst.cmd b/10.01_base/3_test/intrtst.cmd new file mode 100644 index 0000000..4e3bc79 --- /dev/null +++ b/10.01_base/3_test/intrtst.cmd @@ -0,0 +1,185 @@ +# "demo" test sequences for basic interrupt test +# +# Testprogram "intrtst" evaluates chars from DL11, +# but input can be given by deposit char to 7776. + +ti # enter menu +pwr # restart PDP-11 +.wait 1000 +m # emulate missing memory +ll intrtst.lst # load PDP-11 test program + +.print Now start Program on PDP-11. +.print On M9312: "L " , "S" +.input + +.print First all tests with looong blocking ISRs in "Slow" mode +d 7776 'S + +.print ************************************************************************* +.print *** Test 1: single INTR +.print 4 Interrupts raised in order +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +d 7776 '0 +.wait 1000 +i 4 40 +.wait 250 +i 5 50 +.wait 250 +i 6 60 +.wait 250 +i 7 70 +.wait 250 +.print PDP-11 should respond "" "" "" "" +.input + +.print ************************************************************************* +.print *** Test 2: INTR Priority +.print CPU INTR disabled, 4 Interrupts raised and not processed +.print +.print Set CPU level to 7 (all levels BR4..BR7 disabled) +d 7776 '7 +.wait 1000 +i 4 40 +i 5 50 +i 6 60 +i 7 70 +.print PDP-11 should raise no ISRs. +.print Set CPU level to 6, PDP-11 should respond "ISR 70" +d 7776 '6 +.wait 1000 + +.print Set CPU level to 0 +d 7776 '0 +.print PDP-11 should respond "ISR 60" "ISR 50" "ISR 40" +.input + + +.print ************************************************************************* +.print *** Test 3: raise INTR while others are processed +.print The printout "ISR " needs some time and is done on CPU priority level 7 +.print (with INTR disabled). In that time other ISR are requested +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +d 7776 '0 +.wait 1000 +i 4 40 +i 6 60 +i 5 50 +i 7 70 +.print PDP-11 should respond "ISR 40" "ISR 70" "ISR 60" "ISR 50" +.input + + +.print ************************************************************************* +.print *** Test 4: raise INTR multiple +.print The printout "ISR " needs some time and is done on CPU priority level 7 +.print (with INTR disabled). In that time INTR are re-raised again. +.print This is a "CPU interrupt overload" scenario, ISRs are not called as often +.print as required. +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +d 7776 '0 +.wait 1000 +i 4 40 # accepted, ISR called blocking +i 7 70 # ISR 70 scheduled +i 7 70 # re-raise ignored +i 7 70 # re-raise ignored +i 4 40 # ISR 40 scheduled +i 4 40 # re-raise ignored +i 7 70 # re-raise ignored +i 4 40 # re-raise ignored +i 4 40 # re-raise ignored +i 4 40 # re-raise ignored +# end ISR 40: call ISR 70 (highest prio), then ISR 40- +.print PDP-11 should call only two ISRs:"ISR 40", "ISR 70" "ISR 40" +.input + + +.print ************************************************************************* +.print *** Test 5: INTR Slot priority +.print Test: raise INTR while others are processed +.print The printout "ISR " needs some time and is done on CPU priority level 7 +.print (with INTR) disabled. In that time other ISR are requested and waiting. +.print Set CPU level to 7 (all levels BR4..BR7 disabled) +d 7776 '7 +.wait 1000 +# i +i 4 1 110 +i 4 2 120 +i 5 10 70 +i 5 11 100 +i 6 20 50 +i 6 21 60 +i 7 14 40 # is first (waiting on PRU, highest prio), not sorted as not blocked +i 7 12 20 +i 7 11 10 +i 7 13 30 +.print Set CPU level to 0 +d 7776 '0 +.print PDP-11 should process ISRs sorted first by level, then by slot:"ISR 40, 10, 20, ... 120" +.input + +.print *** Test 6: repeat INTR Slot priority, fast ISR +d 7776 'F +d 7776 '7 +.wait 1000 +# i +i 4 1 110 # is #4 +i 4 2 120 +i 5 10 70 # is #3 +i 5 11 100 +i 6 20 50 # is #2 (waiting on PRU, prio after 7) +i 6 21 60 +i 7 14 40 # is #1 (waiting on PRU, highest prio), not sorted as not blocked +i 7 12 20 +i 7 11 10 +i 7 13 30 +.print Set CPU level to 0 +d 7776 '0 +.print PDP-11 should process ISRs sorted first by level, then by slot:"ISR 40, 10, 20, ... 120" +d 7776 'F # display ISR log +.input + + +.print ************************************************************************* +.print *** Test 7: DMA priority +.print Start multi-chunk DMA "1" on less priorized slot, +.print then one "2" in priorized slot into same memory. +.print DMA "2" is interrupting DMA "1" on firstchunk of "1" + +# dma is non blocking and DEPOSITing +# dma +# channel #0 has higher slot prority than #1 + +dma 1 100000 757776 1 +dma 0 100000 757776 2 + +.wait 1000 # wait for DMAs to complete +.print Check: So first data words are "2" (DMA channel 1 runs 2nd). +e 100000 +.print Last data words are "1" (DMA channel 1 finished later and overwrote channel 0's data) +e 757776 +.input + + +d 7776 '0 # all INTR enabled +.wait 1000 +d 7776 'S # slow ISR is printing text +.wait 1000 + +.print ************************************************************************* +.print Test 8: DMA and INTR mixed +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +.print Verify in logic analyzer, that DMA interupts +.print while ISR 70 is printing its text. +.print Trigger LA on 2nd SACK H->L +.input +i 7 70 +dma 0 100000 757776 123 # trigger on 2nd SACK +i 4 40 +.print Is Interrupt BR4 40 services after first DMA chunk or at the end (both OK!) + + + + + + diff --git a/10.01_base/3_test/intrtst.lst.hex b/10.01_base/3_test/intrtst.lst.hex index bf2585d..eb49368 100644 --- a/10.01_base/3_test/intrtst.lst.hex +++ b/10.01_base/3_test/intrtst.lst.hex @@ -7,50 +7,57 @@ 7 ; 1.1. print a start message, 8 ; 1.2. echoes chars typed to the output until ^C is hit 9 ; Chars 0..7 set the new processor priority level. - 10 ; 1.3. prints an end message and HALTs. + 10 ; 1.3 prints an end message and HALTs. 11 ; 1.4. on CONT it repeats. 12 ; 13 ; 2. For INTR test, the 256 vectors 0,4,10,14,..374 each print 14 ; a string on interrupt. - 15 ; - 16 ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com - 17 - 18 FF70h dladr = 177560 ; DL11 console base address - 19 FFFEh psw = 177776 ; processor status - 20 - 21 - 22 ; count of automatically generated interrupt vectors - 23 0040h veccnt = 100 + 15 ; In "Slow" mode, the ISR prints the vector directly + 16 ; (ISR rountine > 100ms) + 17 ; In "Fast" mode, the ISR logs the vector in a list + 18 ; (It can be printed later with "F" + 19 ; + 20 ; As alternative to input over DL11, + 21 ; "keyboard input chars" can be deposited into 7776 + 22 ; + 23 ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com 24 - 25 ; ------- macro to define interrupt vector # ------ - 26 .macro vector vecidx - 27 .=4*vecidx ; vector #vecidx - 28 .word isr'vecidx ; new PC of ISR - 29 .word 340 ; new PSW: priority is max = 7 - 30 .endm + 25 FF70h dladr = 177560 ; DL11 console base address + 26 FFFEh psw = 177776 ; processor status + 27 + 28 + 29 ; count of automatically generated interrupt vectors + 30 0040h veccnt = 100 31 - 32 ; ----- macro to define ISR for vector #n ------- - 33 .macro isr vecidx - 34 isr'vecidx: - 35 mov r0,-(sp) - 36 mov #vecidx*4,r0 ; vector in r0 - 37 call @#doisr ; print message for vector in r0 - 38 mov (sp)+,r0 - 39 rti - 40 .endm - 41 - 42 - 43 - 44 - 45 .asect - 46 - 47 0000h . = 0 - 48 ; ---- "veccnt" Interrupt Vectors --------- - 49 0000h n=0 - 50 .rept veccnt - 51 vector \n - 52 n=n+1 - 53 .endr + 32 ; ------- macro to define interrupt vector # ------ + 33 .macro vector vecidx + 34 .=4*vecidx ; vector #vecidx + 35 .word isr'vecidx ; new PC of ISR + 36 .word 340 ; new PSW: priority is max = 7 + 37 .endm + 38 + 39 ; ----- macro to define ISR for vector #n ------- + 40 .macro isr vecidx + 41 isr'vecidx: + 42 mov r0,-(sp) + 43 mov #vecidx*4,r0 ; vector in r0 + 44 call @#doisr ; print message for vector in r0 + 45 mov (sp)+,r0 + 46 rti + 47 .endm + 48 + 49 + 50 + 51 + 52 .asect + 53 + 54 0000h . = 0 + 55 ; ---- "veccnt" Interrupt Vectors --------- + 56 0000h n=0 + 57 .rept veccnt + 58 vector \n + 59 n=n+1 + 60 .endr 1 vector \n 1 0000h .=4*0 ; vector #0 2 0000h 0100h .word isr0 ; new PC of ISR @@ -371,18 +378,18 @@ 2 00FCh 0472h .word isr77 ; new PC of ISR 3 00FEh 00E0h .word 340 ; new PSW: priority is max = 7 2 0040h n=n+1 - 54 - 55 ; ---- veccnt ISRs --------- - 56 0000h n=0 - 57 .rept veccnt - 58 isr \n - 59 n=n+1 - 60 .endr + 61 + 62 ; ---- veccnt ISRs --------- + 63 0000h n=0 + 64 .rept veccnt + 65 isr \n + 66 n=n+1 + 67 .endr 1 isr \n 1 isr0: 2 0100h 1026h mov r0,-(sp) 3 0102h 15C0h 0000h mov #0*4,r0 ; vector in r0 - 4 0106h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0106h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 010Ah 1580h mov (sp)+,r0 6 010Ch 0002h rti 2 0001h n=n+1 @@ -390,7 +397,7 @@ 1 isr1: 2 010Eh 1026h mov r0,-(sp) 3 0110h 15C0h 0004h mov #1*4,r0 ; vector in r0 - 4 0114h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0114h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0118h 1580h mov (sp)+,r0 6 011Ah 0002h rti 2 0002h n=n+1 @@ -398,7 +405,7 @@ 1 isr2: 2 011Ch 1026h mov r0,-(sp) 3 011Eh 15C0h 0008h mov #2*4,r0 ; vector in r0 - 4 0122h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0122h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0126h 1580h mov (sp)+,r0 6 0128h 0002h rti 2 0003h n=n+1 @@ -406,7 +413,7 @@ 1 isr3: 2 012Ah 1026h mov r0,-(sp) 3 012Ch 15C0h 000Ch mov #3*4,r0 ; vector in r0 - 4 0130h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0130h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0134h 1580h mov (sp)+,r0 6 0136h 0002h rti 2 0004h n=n+1 @@ -414,7 +421,7 @@ 1 isr4: 2 0138h 1026h mov r0,-(sp) 3 013Ah 15C0h 0010h mov #4*4,r0 ; vector in r0 - 4 013Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 013Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0142h 1580h mov (sp)+,r0 6 0144h 0002h rti 2 0005h n=n+1 @@ -422,7 +429,7 @@ 1 isr5: 2 0146h 1026h mov r0,-(sp) 3 0148h 15C0h 0014h mov #5*4,r0 ; vector in r0 - 4 014Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 014Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0150h 1580h mov (sp)+,r0 6 0152h 0002h rti 2 0006h n=n+1 @@ -430,7 +437,7 @@ 1 isr6: 2 0154h 1026h mov r0,-(sp) 3 0156h 15C0h 0018h mov #6*4,r0 ; vector in r0 - 4 015Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 015Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 015Eh 1580h mov (sp)+,r0 6 0160h 0002h rti 2 0007h n=n+1 @@ -438,7 +445,7 @@ 1 isr7: 2 0162h 1026h mov r0,-(sp) 3 0164h 15C0h 001Ch mov #7*4,r0 ; vector in r0 - 4 0168h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0168h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 016Ch 1580h mov (sp)+,r0 6 016Eh 0002h rti 2 0008h n=n+1 @@ -446,7 +453,7 @@ 1 isr10: 2 0170h 1026h mov r0,-(sp) 3 0172h 15C0h 0020h mov #10*4,r0 ; vector in r0 - 4 0176h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0176h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 017Ah 1580h mov (sp)+,r0 6 017Ch 0002h rti 2 0009h n=n+1 @@ -454,7 +461,7 @@ 1 isr11: 2 017Eh 1026h mov r0,-(sp) 3 0180h 15C0h 0024h mov #11*4,r0 ; vector in r0 - 4 0184h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0184h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0188h 1580h mov (sp)+,r0 6 018Ah 0002h rti 2 000Ah n=n+1 @@ -462,7 +469,7 @@ 1 isr12: 2 018Ch 1026h mov r0,-(sp) 3 018Eh 15C0h 0028h mov #12*4,r0 ; vector in r0 - 4 0192h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0192h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0196h 1580h mov (sp)+,r0 6 0198h 0002h rti 2 000Bh n=n+1 @@ -470,7 +477,7 @@ 1 isr13: 2 019Ah 1026h mov r0,-(sp) 3 019Ch 15C0h 002Ch mov #13*4,r0 ; vector in r0 - 4 01A0h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01A0h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01A4h 1580h mov (sp)+,r0 6 01A6h 0002h rti 2 000Ch n=n+1 @@ -478,7 +485,7 @@ 1 isr14: 2 01A8h 1026h mov r0,-(sp) 3 01AAh 15C0h 0030h mov #14*4,r0 ; vector in r0 - 4 01AEh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01AEh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01B2h 1580h mov (sp)+,r0 6 01B4h 0002h rti 2 000Dh n=n+1 @@ -486,7 +493,7 @@ 1 isr15: 2 01B6h 1026h mov r0,-(sp) 3 01B8h 15C0h 0034h mov #15*4,r0 ; vector in r0 - 4 01BCh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01BCh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01C0h 1580h mov (sp)+,r0 6 01C2h 0002h rti 2 000Eh n=n+1 @@ -494,7 +501,7 @@ 1 isr16: 2 01C4h 1026h mov r0,-(sp) 3 01C6h 15C0h 0038h mov #16*4,r0 ; vector in r0 - 4 01CAh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01CAh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01CEh 1580h mov (sp)+,r0 6 01D0h 0002h rti 2 000Fh n=n+1 @@ -502,7 +509,7 @@ 1 isr17: 2 01D2h 1026h mov r0,-(sp) 3 01D4h 15C0h 003Ch mov #17*4,r0 ; vector in r0 - 4 01D8h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01D8h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01DCh 1580h mov (sp)+,r0 6 01DEh 0002h rti 2 0010h n=n+1 @@ -510,7 +517,7 @@ 1 isr20: 2 01E0h 1026h mov r0,-(sp) 3 01E2h 15C0h 0040h mov #20*4,r0 ; vector in r0 - 4 01E6h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01E6h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01EAh 1580h mov (sp)+,r0 6 01ECh 0002h rti 2 0011h n=n+1 @@ -518,7 +525,7 @@ 1 isr21: 2 01EEh 1026h mov r0,-(sp) 3 01F0h 15C0h 0044h mov #21*4,r0 ; vector in r0 - 4 01F4h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01F4h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01F8h 1580h mov (sp)+,r0 6 01FAh 0002h rti 2 0012h n=n+1 @@ -526,7 +533,7 @@ 1 isr22: 2 01FCh 1026h mov r0,-(sp) 3 01FEh 15C0h 0048h mov #22*4,r0 ; vector in r0 - 4 0202h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0202h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0206h 1580h mov (sp)+,r0 6 0208h 0002h rti 2 0013h n=n+1 @@ -534,7 +541,7 @@ 1 isr23: 2 020Ah 1026h mov r0,-(sp) 3 020Ch 15C0h 004Ch mov #23*4,r0 ; vector in r0 - 4 0210h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0210h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0214h 1580h mov (sp)+,r0 6 0216h 0002h rti 2 0014h n=n+1 @@ -542,7 +549,7 @@ 1 isr24: 2 0218h 1026h mov r0,-(sp) 3 021Ah 15C0h 0050h mov #24*4,r0 ; vector in r0 - 4 021Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 021Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0222h 1580h mov (sp)+,r0 6 0224h 0002h rti 2 0015h n=n+1 @@ -550,7 +557,7 @@ 1 isr25: 2 0226h 1026h mov r0,-(sp) 3 0228h 15C0h 0054h mov #25*4,r0 ; vector in r0 - 4 022Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 022Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0230h 1580h mov (sp)+,r0 6 0232h 0002h rti 2 0016h n=n+1 @@ -558,7 +565,7 @@ 1 isr26: 2 0234h 1026h mov r0,-(sp) 3 0236h 15C0h 0058h mov #26*4,r0 ; vector in r0 - 4 023Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 023Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 023Eh 1580h mov (sp)+,r0 6 0240h 0002h rti 2 0017h n=n+1 @@ -566,7 +573,7 @@ 1 isr27: 2 0242h 1026h mov r0,-(sp) 3 0244h 15C0h 005Ch mov #27*4,r0 ; vector in r0 - 4 0248h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0248h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 024Ch 1580h mov (sp)+,r0 6 024Eh 0002h rti 2 0018h n=n+1 @@ -574,7 +581,7 @@ 1 isr30: 2 0250h 1026h mov r0,-(sp) 3 0252h 15C0h 0060h mov #30*4,r0 ; vector in r0 - 4 0256h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0256h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 025Ah 1580h mov (sp)+,r0 6 025Ch 0002h rti 2 0019h n=n+1 @@ -582,7 +589,7 @@ 1 isr31: 2 025Eh 1026h mov r0,-(sp) 3 0260h 15C0h 0064h mov #31*4,r0 ; vector in r0 - 4 0264h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0264h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0268h 1580h mov (sp)+,r0 6 026Ah 0002h rti 2 001Ah n=n+1 @@ -590,7 +597,7 @@ 1 isr32: 2 026Ch 1026h mov r0,-(sp) 3 026Eh 15C0h 0068h mov #32*4,r0 ; vector in r0 - 4 0272h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0272h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0276h 1580h mov (sp)+,r0 6 0278h 0002h rti 2 001Bh n=n+1 @@ -598,7 +605,7 @@ 1 isr33: 2 027Ah 1026h mov r0,-(sp) 3 027Ch 15C0h 006Ch mov #33*4,r0 ; vector in r0 - 4 0280h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0280h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0284h 1580h mov (sp)+,r0 6 0286h 0002h rti 2 001Ch n=n+1 @@ -606,7 +613,7 @@ 1 isr34: 2 0288h 1026h mov r0,-(sp) 3 028Ah 15C0h 0070h mov #34*4,r0 ; vector in r0 - 4 028Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 028Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0292h 1580h mov (sp)+,r0 6 0294h 0002h rti 2 001Dh n=n+1 @@ -614,7 +621,7 @@ 1 isr35: 2 0296h 1026h mov r0,-(sp) 3 0298h 15C0h 0074h mov #35*4,r0 ; vector in r0 - 4 029Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 029Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02A0h 1580h mov (sp)+,r0 6 02A2h 0002h rti 2 001Eh n=n+1 @@ -622,7 +629,7 @@ 1 isr36: 2 02A4h 1026h mov r0,-(sp) 3 02A6h 15C0h 0078h mov #36*4,r0 ; vector in r0 - 4 02AAh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02AAh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02AEh 1580h mov (sp)+,r0 6 02B0h 0002h rti 2 001Fh n=n+1 @@ -630,7 +637,7 @@ 1 isr37: 2 02B2h 1026h mov r0,-(sp) 3 02B4h 15C0h 007Ch mov #37*4,r0 ; vector in r0 - 4 02B8h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02B8h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02BCh 1580h mov (sp)+,r0 6 02BEh 0002h rti 2 0020h n=n+1 @@ -638,7 +645,7 @@ 1 isr40: 2 02C0h 1026h mov r0,-(sp) 3 02C2h 15C0h 0080h mov #40*4,r0 ; vector in r0 - 4 02C6h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02C6h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02CAh 1580h mov (sp)+,r0 6 02CCh 0002h rti 2 0021h n=n+1 @@ -646,7 +653,7 @@ 1 isr41: 2 02CEh 1026h mov r0,-(sp) 3 02D0h 15C0h 0084h mov #41*4,r0 ; vector in r0 - 4 02D4h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02D4h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02D8h 1580h mov (sp)+,r0 6 02DAh 0002h rti 2 0022h n=n+1 @@ -654,7 +661,7 @@ 1 isr42: 2 02DCh 1026h mov r0,-(sp) 3 02DEh 15C0h 0088h mov #42*4,r0 ; vector in r0 - 4 02E2h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02E2h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02E6h 1580h mov (sp)+,r0 6 02E8h 0002h rti 2 0023h n=n+1 @@ -662,7 +669,7 @@ 1 isr43: 2 02EAh 1026h mov r0,-(sp) 3 02ECh 15C0h 008Ch mov #43*4,r0 ; vector in r0 - 4 02F0h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02F0h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02F4h 1580h mov (sp)+,r0 6 02F6h 0002h rti 2 0024h n=n+1 @@ -670,7 +677,7 @@ 1 isr44: 2 02F8h 1026h mov r0,-(sp) 3 02FAh 15C0h 0090h mov #44*4,r0 ; vector in r0 - 4 02FEh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02FEh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0302h 1580h mov (sp)+,r0 6 0304h 0002h rti 2 0025h n=n+1 @@ -678,7 +685,7 @@ 1 isr45: 2 0306h 1026h mov r0,-(sp) 3 0308h 15C0h 0094h mov #45*4,r0 ; vector in r0 - 4 030Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 030Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0310h 1580h mov (sp)+,r0 6 0312h 0002h rti 2 0026h n=n+1 @@ -686,7 +693,7 @@ 1 isr46: 2 0314h 1026h mov r0,-(sp) 3 0316h 15C0h 0098h mov #46*4,r0 ; vector in r0 - 4 031Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 031Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 031Eh 1580h mov (sp)+,r0 6 0320h 0002h rti 2 0027h n=n+1 @@ -694,7 +701,7 @@ 1 isr47: 2 0322h 1026h mov r0,-(sp) 3 0324h 15C0h 009Ch mov #47*4,r0 ; vector in r0 - 4 0328h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0328h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 032Ch 1580h mov (sp)+,r0 6 032Eh 0002h rti 2 0028h n=n+1 @@ -702,7 +709,7 @@ 1 isr50: 2 0330h 1026h mov r0,-(sp) 3 0332h 15C0h 00A0h mov #50*4,r0 ; vector in r0 - 4 0336h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0336h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 033Ah 1580h mov (sp)+,r0 6 033Ch 0002h rti 2 0029h n=n+1 @@ -710,7 +717,7 @@ 1 isr51: 2 033Eh 1026h mov r0,-(sp) 3 0340h 15C0h 00A4h mov #51*4,r0 ; vector in r0 - 4 0344h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0344h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0348h 1580h mov (sp)+,r0 6 034Ah 0002h rti 2 002Ah n=n+1 @@ -718,7 +725,7 @@ 1 isr52: 2 034Ch 1026h mov r0,-(sp) 3 034Eh 15C0h 00A8h mov #52*4,r0 ; vector in r0 - 4 0352h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0352h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0356h 1580h mov (sp)+,r0 6 0358h 0002h rti 2 002Bh n=n+1 @@ -726,7 +733,7 @@ 1 isr53: 2 035Ah 1026h mov r0,-(sp) 3 035Ch 15C0h 00ACh mov #53*4,r0 ; vector in r0 - 4 0360h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0360h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0364h 1580h mov (sp)+,r0 6 0366h 0002h rti 2 002Ch n=n+1 @@ -734,7 +741,7 @@ 1 isr54: 2 0368h 1026h mov r0,-(sp) 3 036Ah 15C0h 00B0h mov #54*4,r0 ; vector in r0 - 4 036Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 036Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0372h 1580h mov (sp)+,r0 6 0374h 0002h rti 2 002Dh n=n+1 @@ -742,7 +749,7 @@ 1 isr55: 2 0376h 1026h mov r0,-(sp) 3 0378h 15C0h 00B4h mov #55*4,r0 ; vector in r0 - 4 037Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 037Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0380h 1580h mov (sp)+,r0 6 0382h 0002h rti 2 002Eh n=n+1 @@ -750,7 +757,7 @@ 1 isr56: 2 0384h 1026h mov r0,-(sp) 3 0386h 15C0h 00B8h mov #56*4,r0 ; vector in r0 - 4 038Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 038Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 038Eh 1580h mov (sp)+,r0 6 0390h 0002h rti 2 002Fh n=n+1 @@ -758,7 +765,7 @@ 1 isr57: 2 0392h 1026h mov r0,-(sp) 3 0394h 15C0h 00BCh mov #57*4,r0 ; vector in r0 - 4 0398h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0398h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 039Ch 1580h mov (sp)+,r0 6 039Eh 0002h rti 2 0030h n=n+1 @@ -766,7 +773,7 @@ 1 isr60: 2 03A0h 1026h mov r0,-(sp) 3 03A2h 15C0h 00C0h mov #60*4,r0 ; vector in r0 - 4 03A6h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03A6h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03AAh 1580h mov (sp)+,r0 6 03ACh 0002h rti 2 0031h n=n+1 @@ -774,7 +781,7 @@ 1 isr61: 2 03AEh 1026h mov r0,-(sp) 3 03B0h 15C0h 00C4h mov #61*4,r0 ; vector in r0 - 4 03B4h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03B4h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03B8h 1580h mov (sp)+,r0 6 03BAh 0002h rti 2 0032h n=n+1 @@ -782,7 +789,7 @@ 1 isr62: 2 03BCh 1026h mov r0,-(sp) 3 03BEh 15C0h 00C8h mov #62*4,r0 ; vector in r0 - 4 03C2h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03C2h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03C6h 1580h mov (sp)+,r0 6 03C8h 0002h rti 2 0033h n=n+1 @@ -790,7 +797,7 @@ 1 isr63: 2 03CAh 1026h mov r0,-(sp) 3 03CCh 15C0h 00CCh mov #63*4,r0 ; vector in r0 - 4 03D0h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03D0h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03D4h 1580h mov (sp)+,r0 6 03D6h 0002h rti 2 0034h n=n+1 @@ -798,7 +805,7 @@ 1 isr64: 2 03D8h 1026h mov r0,-(sp) 3 03DAh 15C0h 00D0h mov #64*4,r0 ; vector in r0 - 4 03DEh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03DEh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03E2h 1580h mov (sp)+,r0 6 03E4h 0002h rti 2 0035h n=n+1 @@ -806,7 +813,7 @@ 1 isr65: 2 03E6h 1026h mov r0,-(sp) 3 03E8h 15C0h 00D4h mov #65*4,r0 ; vector in r0 - 4 03ECh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03ECh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03F0h 1580h mov (sp)+,r0 6 03F2h 0002h rti 2 0036h n=n+1 @@ -814,7 +821,7 @@ 1 isr66: 2 03F4h 1026h mov r0,-(sp) 3 03F6h 15C0h 00D8h mov #66*4,r0 ; vector in r0 - 4 03FAh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03FAh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03FEh 1580h mov (sp)+,r0 6 0400h 0002h rti 2 0037h n=n+1 @@ -822,7 +829,7 @@ 1 isr67: 2 0402h 1026h mov r0,-(sp) 3 0404h 15C0h 00DCh mov #67*4,r0 ; vector in r0 - 4 0408h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0408h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 040Ch 1580h mov (sp)+,r0 6 040Eh 0002h rti 2 0038h n=n+1 @@ -830,7 +837,7 @@ 1 isr70: 2 0410h 1026h mov r0,-(sp) 3 0412h 15C0h 00E0h mov #70*4,r0 ; vector in r0 - 4 0416h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0416h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 041Ah 1580h mov (sp)+,r0 6 041Ch 0002h rti 2 0039h n=n+1 @@ -838,7 +845,7 @@ 1 isr71: 2 041Eh 1026h mov r0,-(sp) 3 0420h 15C0h 00E4h mov #71*4,r0 ; vector in r0 - 4 0424h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0424h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0428h 1580h mov (sp)+,r0 6 042Ah 0002h rti 2 003Ah n=n+1 @@ -846,7 +853,7 @@ 1 isr72: 2 042Ch 1026h mov r0,-(sp) 3 042Eh 15C0h 00E8h mov #72*4,r0 ; vector in r0 - 4 0432h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0432h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0436h 1580h mov (sp)+,r0 6 0438h 0002h rti 2 003Bh n=n+1 @@ -854,7 +861,7 @@ 1 isr73: 2 043Ah 1026h mov r0,-(sp) 3 043Ch 15C0h 00ECh mov #73*4,r0 ; vector in r0 - 4 0440h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0440h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0444h 1580h mov (sp)+,r0 6 0446h 0002h rti 2 003Ch n=n+1 @@ -862,7 +869,7 @@ 1 isr74: 2 0448h 1026h mov r0,-(sp) 3 044Ah 15C0h 00F0h mov #74*4,r0 ; vector in r0 - 4 044Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 044Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0452h 1580h mov (sp)+,r0 6 0454h 0002h rti 2 003Dh n=n+1 @@ -870,7 +877,7 @@ 1 isr75: 2 0456h 1026h mov r0,-(sp) 3 0458h 15C0h 00F4h mov #75*4,r0 ; vector in r0 - 4 045Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 045Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0460h 1580h mov (sp)+,r0 6 0462h 0002h rti 2 003Eh n=n+1 @@ -878,7 +885,7 @@ 1 isr76: 2 0464h 1026h mov r0,-(sp) 3 0466h 15C0h 00F8h mov #76*4,r0 ; vector in r0 - 4 046Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 046Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 046Eh 1580h mov (sp)+,r0 6 0470h 0002h rti 2 003Fh n=n+1 @@ -886,263 +893,430 @@ 1 isr77: 2 0472h 1026h mov r0,-(sp) 3 0474h 15C0h 00FCh mov #77*4,r0 ; vector in r0 - 4 0478h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0478h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 047Ch 1580h mov (sp)+,r0 6 047Eh 0002h rti 2 0040h n=n+1 - 61 - 62 - 63 - 64 - 65 ; ---- foreground thread --------- - 66 1000h .=10000 - 67 - 68 0FFEh stack = . - 2 ; stack growns down from start + 68 69 - 70 start: - 71 1000h 15C6h 0FFEh mov #stack,sp ; init stack - 72 1004h 0A1Fh FFFEh clr @#psw ; clear priority, allow all interupts - 73 - 74 ; 1. print "Hello" msg - 75 1008h 15C1h 1104h mov #shello,r1 - 76 100Ch 09DFh 1096h call @#puts - 77 - 78 ; test vecnum printout - 79 ; mov #123456,r0 - 80 ; call @#isrmsg - 81 - 82 ; 2. echo chars until ^C hit - 83 1$: - 84 1010h 09DFh 10ECh call @#getc ; wait for char, return in r0 - 85 1014h 45C0h FF80h bic #177600,r0 ; make 7bit: clear bits <15:7> - 86 1018h A017h 0003h cmpb r0,#3 ; break by ^C ? - 87 101Ch 0320h beq 9$ ; yes: leave loop - 88 101Eh A017h 0030h cmpb r0,#60 - 89 1022h 871Ah blo 2$ ; char is < '0' - 90 1024h A017h 0037h cmpb r0,#67 - 91 1028h 8217h bhi 2$ ; char is > '7' - 92 102Ah 9002h movb r0,r2 ; save - 93 - 94 ; key is 0..7: change priority - 95 102Ch 15C1h 11BCh mov #sprio0,r1 ; print info - 96 1030h 09DFh 1096h call @#puts - 97 1034h 9080h movb r2,r0 ; restore digit - 98 1036h 09DFh 10DCh call @#putc ; print level digit - 99 103Ah 15C1h 11D0h mov #sprio1,r1 - 100 103Eh 09DFh 1096h call @#puts - 101 ; change PSW - 102 1042h 9080h movb r2,r0 - 103 1044h C5C0h 00F8h bicb #370,r0 ; ASCII -> integer - 104 1048h 0CC0h asl r0 ; move level to "priority" field in PSW - 105 104Ah 0CC0h asl r0 ; in bits 7,6,5 - 106 104Ch 0CC0h asl r0 - 107 104Eh 0CC0h asl r0 - 108 1050h 0CC0h asl r0 - 109 1052h 101Fh FFFEh mov r0,@#psw + 70 + 71 + 72 ; ---- foreground thread --------- + 73 0000h .=0 + 74 0000h 005Fh 1000h jmp @#start ; easier manual start from 0 + 75 + 76 0FFEh .=7776 + 77 0FFEh 0000h inchr: .word ; input alternative to DL11 + 78 + 79 1000h .=10000 + 80 + 81 0FFEh stack = . - 2 ; stack growns down from start + 82 + 83 start: + 84 1000h 15C6h 0FFEh mov #stack,sp ; init stack + 85 1004h 0A1Fh FFFEh clr @#psw ; clear priority, allow all interupts + 86 + 87 1008h 0005h reset ; disable INTR on all devices + 88 + 89 100Ah 0A1Fh 1346h clr @#logptr ; default: slow mode + 90 + 91 ; 1. print "Hello" msg + 92 100Eh 15C1h 11A0h mov #shello,r1 + 93 1012h 09DFh 110Ah call @#puts + 94 + 95 ; test vecnum printout + 96 ; mov #123456,r0 + 97 ; call @#isrmsg + 98 + 99 ; 2. echo chars until ^C hit + 100 1$: + 101 1016h 09DFh 1170h call @#getc ; wait for char, return in r0 + 102 101Ah 45C0h FF80h bic #177600,r0 ; make 7bit: clear bits <15:7> + 103 101Eh A017h 0003h cmpb r0,#3 ; break by ^C ? + 104 1022h 0337h beq 9$ ; yes: leave loop + 105 1024h A017h 0030h cmpb r0,#60 + 106 1028h 871Ah blo 2$ ; char is < '0' + 107 102Ah A017h 0037h cmpb r0,#67 + 108 102Eh 8217h bhi 2$ ; char is > '7' + 109 1030h 9002h movb r0,r2 ; save 110 - 111 1056h 01DCh br 1$ - 112 - 113 2$: - 114 1058h 09DFh 10DCh call @#putc ; no: echo char in r0 and loop - 115 105Ch 01D9h br 1$ - 116 - 117 9$: - 118 - 119 ; 3. print "Bye bye" msg and HALT - 120 105Eh 15C1h 11D3h mov #sbye,r1 - 121 1062h 09DFh 1096h call @#puts - 122 1066h 0000h halt - 123 - 124 ; 4. loop on CONT - 125 1068h 01CBh br start - 126 + 111 ; key is 0..7: change priority + 112 1032h 15C1h 131Bh mov #sprio0,r1 ; print info + 113 1036h 09DFh 110Ah call @#puts + 114 103Ah 9080h movb r2,r0 ; restore digit + 115 103Ch 09DFh 1160h call @#putc ; print level digit + 116 1040h 15C1h 1335h mov #sprio1,r1 + 117 1044h 09DFh 110Ah call @#puts + 118 ; change PSW + 119 1048h 9080h movb r2,r0 + 120 104Ah C5C0h 00F8h bicb #370,r0 ; ASCII -> integer + 121 104Eh 0CC0h asl r0 ; move level to "priority" field in PSW + 122 1050h 0CC0h asl r0 ; in bits 7,6,5 + 123 1052h 0CC0h asl r0 + 124 1054h 0CC0h asl r0 + 125 1056h 0CC0h asl r0 + 126 1058h 101Fh FFFEh mov r0,@#psw 127 - 128 ; ---------------------- - 129 ; Common code for all ISRs - 130 ; print vector number in r0 - 131 ; called on interrupt level - 132 - 133 doisr: - 134 106Ah 1126h mov r4,-(sp) - 135 106Ch 10E6h mov r3,-(sp) - 136 106Eh 10A6h mov r2,-(sp) - 137 1070h 1066h mov r1,-(sp) - 138 1072h 1026h mov r0,-(sp) - 139 - 140 ; print msg - 141 1074h 15C1h 10FAh mov #sisr1,r1 ; "ISR " - 142 1078h 09DFh 1096h call @#puts - 143 107Ch 1380h mov (sp),r0 ; restore vecnum - 144 107Eh 09DFh 10B2h call @#putnum - 145 1082h 15C1h 1100h mov #sisr2,r1 ; "cr lf - 146 1086h 09DFh 1096h call @#puts - 147 - 148 108Ah 1580h mov (sp)+,r0 - 149 108Ch 1581h mov (sp)+,r1 - 150 108Eh 1582h mov (sp)+,r2 - 151 1090h 1583h mov (sp)+,r3 - 152 1092h 1584h mov (sp)+,r4 - 153 1094h 0087h return - 154 + 128 105Ch 01DCh br 1$ ; OK, next char + 129 + 130 2$: ; -- eval "S", F" + 131 105Eh A017h 0053h cmpb r0,#'S + 132 1062h 0207h bne 3$ + 133 1064h 15C1h 12C0h mov #sslwmd,r1 ; "slow mode" + 134 1068h 09DFh 110Ah call @#puts + 135 106Ch 0A1Fh 1346h clr @#logptr + 136 1070h 01D2h br 1$ ; OK, next char + 137 1072h A017h 0046h 3$: cmpb r0,#'F + 138 1076h 020Ah bne 8$ ; + 139 1078h 15C1h 12EFh mov #sfstmd,r1 ; "fast mode" + 140 107Ch 09DFh 110Ah call @#puts + 141 1080h 09DFh 10E0h call @#prtlog ; print logged vectors, if any + 142 1084h 15DFh 1348h 1346h mov #logbuf,@#logptr; pointer to biuffer start + 143 108Ah 01C5h br 1$ ; OK, next char + 144 + 145 8$: + 146 108Ch 09DFh 1160h call @#putc ; no: echo char in r0 and loop + 147 1090h 01C2h br 1$ + 148 + 149 9$: + 150 + 151 ; 3. print "Bye bye" msg and HALT + 152 1092h 15C1h 1338h mov #sbye,r1 + 153 1096h 09DFh 110Ah call @#puts + 154 109Ah 0000h halt 155 - 156 ; ---------------------- - 157 ; puts - print a string - 158 ; r1 = pointer, r0,r1 changed - 159 puts: - 160 1096h 9440h movb (r1)+,r0 ; load xmt char - 161 1098h 0303h beq 1$ ; string ends with 0 - 162 109Ah 09DFh 10DCh call @#putc - 163 109Eh 01FBh br puts ; transmit nxt char of string - 164 10A0h 0087h 1$: return - 165 - 166 - 167 ; ---------------------- - 168 ; putnum - print the octal number in r0 - 169 10A2h numbf0: .blkw 10 ; space to mount number string - 170 10B2h numbf1 =. - 171 putnum: - 172 10B2h 1002h mov r0,r2 ; r2 = shifter - 173 10B4h 15C1h 10B2h mov #numbf1,r1 ; r1 = buffer pointer, backwards - 174 10B8h 95E1h 0000h movb #0,-(r1) ; set terminating 0 - 175 ; repeat 6 times - 176 10BCh 15C3h 0006h mov #6,r3 - 177 1$: - 178 10C0h 1080h mov r2,r0 - 179 ; extract lower 3 bits = octal digit - 180 10C2h 45C0h FFF8h bic #177770,r0 ; r0 &= 0x07 - 181 10C6h 65C0h 0030h add #60,r0 ; r0 += '0' - 182 10CAh 9021h movb r0,-(r1) ; write in buffer - 183 10CCh 00A1h clc - 184 10CEh 0C02h ror r2 ; shift to next digit - 185 10D0h 0C02h ror r2 - 186 10D2h 0C02h ror r2 - 187 10D4h 7ECBh sob r3,1$ ; loop for all 6 digits - 188 - 189 10D6h 09DFh 1096h call @#puts - 190 10DAh 0087h return - 191 - 192 - 193 ; DEC DL11 console I/O - 194 ; ---------------------- - 195 ; putc - output a single char - 196 ; r0 = char, r4 changed - 197 putc: - 198 10DCh 15C4h FF70h mov #dladr,r4 ; set base addr - 199 10E0h 9034h 0006h movb r0,6(r4) ; char into transmit buffer - 200 10E4h 8BF4h 0004h 1$: tstb 4(r4) ; XMT RDY? - 201 10E8h 80FDh bpl 1$ ; no, loop - 202 10EAh 0087h return - 203 - 204 ; ---------------------- - 205 ; getc - input a single char - 206 ; result in r0, r4 changed - 207 getc: - 208 10ECh 15C4h FF70h mov #dladr,r4 ; set base addr - 209 10F0h 8BCCh 1$: tstb (r4) ; RCVR DONE? - 210 10F2h 80FEh bpl 1$ ; no, loop - 211 10F4h 1D00h 0002h mov 2(r4),r0 ; return data - 212 10F8h 0087h return - 213 - 214 - 215 ; ---- Test strings, each 256 chars max --------- - 216 - 217 sisr1: ; start of ISR message - 218 10FAh 3Ch 49h 53h .ascii / message - 221 1100h 3Eh .ascii />/ - 222 1101h 0Dh 0Ah .byte 15,12 ; CR, LF, - 223 1103h 00h .byte 0 ; NUL=end marker - 224 - 225 - 226 shello: - 227 1104h 0Dh 0Ah .byte 15,12 ; CR, LF, - 228 1106h 2Ah 2Ah 2Ah .ascii /*** Interrupt and DMA test ***/ - 1109h 20h 49h 6Eh - 110Ch 74h 65h 72h - 110Fh 72h 75h 70h - 1112h 74h 20h 61h - 1115h 6Eh 64h 20h - 1118h 44h 4Dh 41h - 111Bh 20h 74h 65h - 111Eh 73h 74h 20h - 1121h 2Ah 2Ah 2Ah - 229 1124h 0Dh 0Ah .byte 15,12 ; CR, LF, - 230 1126h 54h 68h 65h .ascii /The INTR vectors 0..77 print the vector num./ - 1129h 20h 49h 4Eh - 112Ch 54h 52h 20h - 112Fh 76h 65h 63h - 1132h 74h 6Fh 72h - 1135h 73h 20h 30h - 1138h 2Eh 2Eh 37h - 113Bh 37h 20h 70h - 113Eh 72h 69h 6Eh - 1141h 74h 20h 74h - 1144h 68h 65h 20h - 1147h 76h 65h 63h - 114Ah 74h 6Fh 72h - 114Dh 20h 6Eh 75h - 1150h 6Dh 2Eh - 231 1152h 0Dh 0Ah .byte 15,12 ; CR, LF, - 232 1154h 54h 68h 65h .ascii /The foreground thread echoes typed chars, ^C HALTs./ - 1157h 20h 66h 6Fh - 115Ah 72h 65h 67h - 115Dh 72h 6Fh 75h - 1160h 6Eh 64h 20h - 1163h 74h 68h 72h - 1166h 65h 61h 64h - 1169h 20h 65h 63h - 116Ch 68h 6Fh 65h - 116Fh 73h 20h 74h - 1172h 79h 70h 65h - 1175h 64h 20h 63h - 1178h 68h 61h 72h - 117Bh 73h 2Ch 20h - 117Eh 5Eh 43h 20h - 1181h 48h 41h 4Ch - 1184h 54h 73h 2Eh - 233 1187h 0Dh 0Ah .byte 15,12 ; CR, LF, - 234 1189h 43h 68h 61h .ascii /Chars 0..7 set the new processor priority level./ - 118Ch 72h 73h 20h - 118Fh 30h 2Eh 2Eh - 1192h 37h 20h 73h - 1195h 65h 74h 20h - 1198h 74h 68h 65h - 119Bh 20h 6Eh 65h - 119Eh 77h 20h 70h - 11A1h 72h 6Fh 63h - 11A4h 65h 73h 73h - 11A7h 6Fh 72h 20h - 11AAh 70h 72h 69h - 11ADh 6Fh 72h 69h - 11B0h 74h 79h 20h - 11B3h 6Ch 65h 76h - 11B6h 65h 6Ch 2Eh - 235 11B9h 0Dh 0Ah .byte 15,12 ; CR, LF, - 236 11BBh 00h .byte 0 ; NUL=end marker - 237 - 238 sprio0: - 239 11BCh 0Dh 0Ah .byte 15,12 ; CR, LF, - 240 11BEh 43h 50h 55h .ascii /CPU priority now / - 11C1h 20h 70h 72h - 11C4h 69h 6Fh 72h - 11C7h 69h 74h 79h - 11CAh 20h 6Eh 6Fh - 11CDh 77h 20h - 241 11CFh 00h .byte 0 - 242 - 243 sprio1: - 244 11D0h 0Dh 0Ah .byte 15,12 ; CR, LF, - 245 11D2h 00h .byte 0 ; NUL=end marker - 246 - 247 sbye: - 248 11D3h 0Dh 0Ah .byte 15,12 - 249 11D5h 47h 6Fh 6Fh .ascii /Good Bye!/ - 11D8h 64h 20h 42h - 11DBh 79h 65h 21h - 250 11DEh 0Dh 0Ah 00h .byte 15,12,0 ; CR, LF, NUL=end marker - 251 - 252 - 253 .end - 253 + 156 ; 4. loop on CONT + 157 109Ch 01B1h br start + 158 + 159 + 160 ; ---------------------- + 161 ; Common code for all ISRs + 162 ; print vector number in r0 + 163 ; called on interrupt level + 164 + 165 doisr: + 166 109Eh 1126h mov r4,-(sp) + 167 10A0h 10E6h mov r3,-(sp) + 168 10A2h 10A6h mov r2,-(sp) + 169 10A4h 1066h mov r1,-(sp) + 170 10A6h 1026h mov r0,-(sp) + 171 + 172 10A8h 17C1h 1346h mov @#logptr,r1 + 173 10ACh 0304h beq 1$ + 174 ; Fast mode: log vector + 175 10AEh 1011h mov r0,(r1)+ ; store vector in array + 176 10B0h 105Fh 1346h mov r1,@#logptr ; save updated list pointer + 177 + 178 10B4h 010Fh br 9$ + 179 1$: ; "Slow mode: print msg + 180 10B6h 15C1h 1196h mov #sisr1,r1 ; "ISR " + 181 10BAh 09DFh 110Ah call @#puts + 182 10BEh 1380h mov (sp),r0 ; restore vecnum + 183 10C0h 09DFh 1126h call @#putnum + 184 10C4h 15C1h 119Ch mov #sisr2,r1 ; "cr lf + 185 10C8h 09DFh 110Ah call @#puts + 186 ; make ISR 100ms + 187 10CCh 15C0h 0064h mov #144,r0 ; 100 + 188 10D0h 09DFh 1188h call @#waitms + 189 + 190 9$: + 191 10D4h 1580h mov (sp)+,r0 + 192 10D6h 1581h mov (sp)+,r1 + 193 10D8h 1582h mov (sp)+,r2 + 194 10DAh 1583h mov (sp)+,r3 + 195 10DCh 1584h mov (sp)+,r4 + 196 10DEh 0087h return + 197 + 198 + 199 ; ---------------------- + 200 ; prtlog - print log of isr vectors + 201 ; word list from logbuf to logptr + 202 prtlog: + 203 10E0h 0BDFh 1346h tst @#logptr + 204 10E4h 0311h beq 9$ ; ptr 0, slow mode, nothing logged + 205 10E6h 15C2h 1348h mov #logbuf,r2 + 206 1$: + 207 10EAh 209Fh 1346h cmp r2,@#logptr + 208 10EEh 8608h bhis 8$ ; end of list reached + 209 10F0h 1480h mov (r2)+,r0 ; print vector from list + 210 10F2h 09DFh 1126h call @#putnum + 211 10F6h 15C0h 0020h mov #40,r0 ; print space separator + 212 10FAh 09DFh 1160h call @#putc + 213 10FEh 01F5h br 1$ + 214 8$: + 215 1100h 15C1h 1343h mov #scrlf,r1 ; CR/LF + 216 1104h 09DFh 110Ah call @#puts + 217 + 218 9$: + 219 1108h 0087h return + 220 + 221 ; ---------------------- + 222 ; puts - print a string + 223 ; r1 = pointer, r0,r1 changed + 224 puts: + 225 110Ah 9440h movb (r1)+,r0 ; load xmt char + 226 110Ch 0303h beq 1$ ; string ends with 0 + 227 110Eh 09DFh 1160h call @#putc + 228 1112h 01FBh br puts ; transmit nxt char of string + 229 1114h 0087h 1$: return + 230 + 231 + 232 ; ---------------------- + 233 ; putnum - print the octal number in r0 + 234 1116h numbf0: .blkw 10 ; space to mount number string + 235 1126h numbf1 =. + 236 putnum: + 237 1126h 1026h mov r0,-(sp) + 238 1128h 1066h mov r1,-(sp) + 239 112Ah 10A6h mov r2,-(sp) + 240 112Ch 10E6h mov r3,-(sp) + 241 112Eh 1002h mov r0,r2 ; r2 = shifter + 242 1130h 15C1h 1126h mov #numbf1,r1 ; r1 = buffer pointer, backwards + 243 1134h 95E1h 0000h movb #0,-(r1) ; set terminating 0 + 244 ; repeat 6 times + 245 1138h 15C3h 0006h mov #6,r3 + 246 1$: + 247 113Ch 1080h mov r2,r0 + 248 ; extract lower 3 bits = octal digit + 249 113Eh 45C0h FFF8h bic #177770,r0 ; r0 &= 0x07 + 250 1142h 65C0h 0030h add #60,r0 ; r0 += '0' + 251 1146h 9021h movb r0,-(r1) ; write in buffer + 252 1148h 00A1h clc + 253 114Ah 0C82h asr r2 ; shift to next digit + 254 114Ch 0C82h asr r2 + 255 114Eh 0C82h asr r2 + 256 1150h 7ECBh sob r3,1$ ; loop for all 6 digits + 257 + 258 1152h 09DFh 110Ah call @#puts + 259 1156h 1583h mov (sp)+,r3 + 260 1158h 1582h mov (sp)+,r2 + 261 115Ah 1581h mov (sp)+,r1 + 262 115Ch 1580h mov (sp)+,r0 + 263 115Eh 0087h return + 264 + 265 + 266 ; DEC DL11 console I/O + 267 ; ---------------------- + 268 ; putc - output a single char + 269 ; r0 = char, r4 changed + 270 putc: + 271 1160h 15C4h FF70h mov #dladr,r4 ; set base addr + 272 1164h 9034h 0006h movb r0,6(r4) ; char into transmit buffer + 273 1168h 8BF4h 0004h 1$: tstb 4(r4) ; XMT RDY? + 274 116Ch 80FDh bpl 1$ ; no, loop + 275 116Eh 0087h return + 276 + 277 ; ---------------------- + 278 ; getc - input a single char + 279 ; result in r0, r4 changed + 280 getc: + 281 1170h 15C4h FF70h mov #dladr,r4 ; set base addr + 282 1174h 17C0h 0FFEh 1$: mov @#inchr,r0 ; external DEPOSIT into inchr? + 283 1178h 0204h bne 9$ ; yes: process as input + 284 117Ah 8BCCh tstb (r4) ; else: RCVR DONE? + 285 117Ch 80FBh bpl 1$ ; no, loop + 286 117Eh 1D00h 0002h mov 2(r4),r0 ; return data + 287 9$: + 288 1182h 0A1Fh 0FFEh clr @#inchr + 289 1186h 0087h return + 290 + 291 ; ---------------------- + 292 ; waitms - wait r0 milli seconds + 293 waitms: + 294 1188h 1066h mov r1,-(sp) + 295 + 296 1$: ; -- outer loop + 297 118Ah 15C1h 01F4h mov #764,r1 ; 500 + 298 2$: ; -- inner loop: 1ms @ 1MHz + 299 118Eh 7E41h sob r1,2$ ; 1 cycle,2 us per loop (11/34) + 300 + 301 1190h 7E04h sob r0,1$ + 302 1192h 1581h mov (sp)+,r1 + 303 1194h 0087h return + 304 + 305 ; ---- Test strings, each 256 chars max --------- + 306 + 307 sisr1: ; start of ISR message + 308 1196h 3Ch 49h 53h .ascii / message + 311 119Ch 3Eh .ascii />/ + 312 119Dh 0Dh 0Ah .byte 15,12 ; CR, LF, + 313 119Fh 00h .byte 0 ; NUL=end marker + 314 + 315 + 316 shello: + 317 11A0h 0Dh 0Ah .byte 15,12 ; CR, LF, + 318 11A2h 2Ah 2Ah 2Ah .ascii /*** Interrupt and DMA test ***/ + 11A5h 20h 49h 6Eh + 11A8h 74h 65h 72h + 11ABh 72h 75h 70h + 11AEh 74h 20h 61h + 11B1h 6Eh 64h 20h + 11B4h 44h 4Dh 41h + 11B7h 20h 74h 65h + 11BAh 73h 74h 20h + 11BDh 2Ah 2Ah 2Ah + 319 11C0h 0Dh 0Ah .byte 15,12 ; CR, LF, + 320 11C2h 54h 68h 65h .ascii /The INTR vectors 0..77 print the vector num./ + 11C5h 20h 49h 4Eh + 11C8h 54h 52h 20h + 11CBh 76h 65h 63h + 11CEh 74h 6Fh 72h + 11D1h 73h 20h 30h + 11D4h 2Eh 2Eh 37h + 11D7h 37h 20h 70h + 11DAh 72h 69h 6Eh + 11DDh 74h 20h 74h + 11E0h 68h 65h 20h + 11E3h 76h 65h 63h + 11E6h 74h 6Fh 72h + 11E9h 20h 6Eh 75h + 11ECh 6Dh 2Eh + 321 11EEh 0Dh 0Ah .byte 15,12 ; CR, LF, + 322 11F0h 54h 68h 65h .ascii /The foreground thread echoes typed chars, ^C HALTs./ + 11F3h 20h 66h 6Fh + 11F6h 72h 65h 67h + 11F9h 72h 6Fh 75h + 11FCh 6Eh 64h 20h + 11FFh 74h 68h 72h + 1202h 65h 61h 64h + 1205h 20h 65h 63h + 1208h 68h 6Fh 65h + 120Bh 73h 20h 74h + 120Eh 79h 70h 65h + 1211h 64h 20h 63h + 1214h 68h 61h 72h + 1217h 73h 2Ch 20h + 121Ah 5Eh 43h 20h + 121Dh 48h 41h 4Ch + 1220h 54h 73h 2Eh + 323 1223h 0Dh 0Ah .byte 15,12 ; CR, LF, + 324 1225h 43h 68h 61h .ascii /Chars 0..7 set the new processor priority level./ + 1228h 72h 73h 20h + 122Bh 30h 2Eh 2Eh + 122Eh 37h 20h 73h + 1231h 65h 74h 20h + 1234h 74h 68h 65h + 1237h 20h 6Eh 65h + 123Ah 77h 20h 70h + 123Dh 72h 6Fh 63h + 1240h 65h 73h 73h + 1243h 6Fh 72h 20h + 1246h 70h 72h 69h + 1249h 6Fh 72h 69h + 124Ch 74h 79h 20h + 124Fh 6Ch 65h 76h + 1252h 65h 6Ch 2Eh + 325 1255h 0Dh 0Ah .byte 15,12 ; CR, LF, + 326 1257h 53h 20h 3Dh .ascii /S = slow mode: ISR prints message (default)/ + 125Ah 20h 73h 6Ch + 125Dh 6Fh 77h 20h + 1260h 6Dh 6Fh 64h + 1263h 65h 3Ah 20h + 1266h 49h 53h 52h + 1269h 20h 70h 72h + 126Ch 69h 6Eh 74h + 126Fh 73h 20h 6Dh + 1272h 65h 73h 73h + 1275h 61h 67h 65h + 1278h 20h 28h 64h + 127Bh 65h 66h 61h + 127Eh 75h 6Ch 74h + 1281h 29h + 327 1282h 0Dh 0Ah .byte 15,12 ; CR, LF, + 328 1284h 46h 20h 3Dh .ascii /F = fast mode: ISR logs vector, print and clr current log/ + 1287h 20h 66h 61h + 128Ah 73h 74h 20h + 128Dh 6Dh 6Fh 64h + 1290h 65h 3Ah 20h + 1293h 49h 53h 52h + 1296h 20h 6Ch 6Fh + 1299h 67h 73h 20h + 129Ch 76h 65h 63h + 129Fh 74h 6Fh 72h + 12A2h 2Ch 20h 70h + 12A5h 72h 69h 6Eh + 12A8h 74h 20h 61h + 12ABh 6Eh 64h 20h + 12AEh 63h 6Ch 72h + 12B1h 20h 63h 75h + 12B4h 72h 72h 65h + 12B7h 6Eh 74h 20h + 12BAh 6Ch 6Fh 67h + 329 12BDh 0Dh 0Ah .byte 15,12 ; CR, LF, + 330 12BFh 00h .byte 0 ; NUL=end marker + 331 + 332 sslwmd: + 333 12C0h 53h 6Ch 6Fh .ascii /Slow mode: called ISR prints vector directly/ + 12C3h 77h 20h 6Dh + 12C6h 6Fh 64h 65h + 12C9h 3Ah 20h 63h + 12CCh 61h 6Ch 6Ch + 12CFh 65h 64h 20h + 12D2h 49h 53h 52h + 12D5h 20h 70h 72h + 12D8h 69h 6Eh 74h + 12DBh 73h 20h 76h + 12DEh 65h 63h 74h + 12E1h 6Fh 72h 20h + 12E4h 64h 69h 72h + 12E7h 65h 63h 74h + 12EAh 6Ch 79h + 334 12ECh 0Dh 0Ah .byte 15,12 ; CR, LF, + 335 12EEh 00h .byte 0 ; NUL=end marker + 336 + 337 sfstmd: + 338 12EFh 46h 61h 73h .ascii /Fast mode: called ISR vectors are logged:/ + 12F2h 74h 20h 6Dh + 12F5h 6Fh 64h 65h + 12F8h 3Ah 20h 63h + 12FBh 61h 6Ch 6Ch + 12FEh 65h 64h 20h + 1301h 49h 53h 52h + 1304h 20h 76h 65h + 1307h 63h 74h 6Fh + 130Ah 72h 73h 20h + 130Dh 61h 72h 65h + 1310h 20h 6Ch 6Fh + 1313h 67h 67h 65h + 1316h 64h 3Ah + 339 1318h 0Dh 0Ah .byte 15,12 ; CR, LF, + 340 131Ah 00h .byte 0 ; NUL=end marker + 341 + 342 sprio0: + 343 131Bh 0Dh 0Ah .byte 15,12 ; CR, LF, + 344 131Dh 43h 50h 55h .ascii /CPU priority level now / + 1320h 20h 70h 72h + 1323h 69h 6Fh 72h + 1326h 69h 74h 79h + 1329h 20h 6Ch 65h + 132Ch 76h 65h 6Ch + 132Fh 20h 6Eh 6Fh + 1332h 77h 20h + 345 1334h 00h .byte 0 + 346 + 347 sprio1: + 348 1335h 0Dh 0Ah .byte 15,12 ; CR, LF, + 349 1337h 00h .byte 0 ; NUL=end marker + 350 + 351 sbye: + 352 1338h 0Dh 0Ah .byte 15,12 + 353 133Ah 47h 6Fh 6Fh .ascii /Good Bye!/ + 133Dh 64h 20h 42h + 1340h 79h 65h 21h + 354 scrlf: + 355 1343h 0Dh 0Ah 00h .byte 15,12,0 ; CR, LF, NUL=end marker + 356 + 357 ; .byte 0 ; make addr even + 358 + 359 ; in "fast" mode, ISR calls fill this array with called vectors + 360 logptr: + 361 1346h 0000h .word ; addr of next logentry to fill. 0 in "slow" mode + 362 + 363 logbuf: + 364 + 365 + 366 .end + 366 diff --git a/10.01_base/3_test/intrtst.mac b/10.01_base/3_test/intrtst.mac index 2f88bcd..710c4e7 100644 --- a/10.01_base/3_test/intrtst.mac +++ b/10.01_base/3_test/intrtst.mac @@ -7,11 +7,18 @@ ; 1.1. print a start message, ; 1.2. echoes chars typed to the output until ^C is hit ; Chars 0..7 set the new processor priority level. - ; 1.3. prints an end message and HALTs. + ; 1.3 prints an end message and HALTs. ; 1.4. on CONT it repeats. ; ; 2. For INTR test, the 256 vectors 0,4,10,14,..374 each print ; a string on interrupt. + ; In "Slow" mode, the ISR prints the vector directly + ; (ISR rountine > 100ms) + ; In "Fast" mode, the ISR logs the vector in a list + ; (It can be printed later with "F" + ; + ; As alternative to input over DL11, + ; "keyboard input chars" can be deposited into 7776 ; ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com @@ -63,6 +70,12 @@ isr'vecidx: ; ---- foreground thread --------- + .=0 + jmp @#start ; easier manual start from 0 + + .=7776 +inchr: .word ; input alternative to DL11 + .=10000 stack = . - 2 ; stack growns down from start @@ -71,6 +84,10 @@ start: mov #stack,sp ; init stack clr @#psw ; clear priority, allow all interupts + reset ; disable INTR on all devices + + clr @#logptr ; default: slow mode + ; 1. print "Hello" msg mov #shello,r1 call @#puts @@ -108,9 +125,24 @@ start: asl r0 mov r0,@#psw - br 1$ + br 1$ ; OK, next char -2$: +2$: ; -- eval "S", F" + cmpb r0,#'S + bne 3$ + mov #sslwmd,r1 ; "slow mode" + call @#puts + clr @#logptr + br 1$ ; OK, next char +3$: cmpb r0,#'F + bne 8$ ; + mov #sfstmd,r1 ; "fast mode" + call @#puts + call @#prtlog ; print logged vectors, if any + mov #logbuf,@#logptr; pointer to biuffer start + br 1$ ; OK, next char + +8$: call @#putc ; no: echo char in r0 and loop br 1$ @@ -137,14 +169,25 @@ doisr: mov r1,-(sp) mov r0,-(sp) - ; print msg + mov @#logptr,r1 + beq 1$ + ; Fast mode: log vector + mov r0,(r1)+ ; store vector in array + mov r1,@#logptr ; save updated list pointer + + br 9$ +1$: ; "Slow mode: print msg mov #sisr1,r1 ; "ISR " call @#puts mov (sp),r0 ; restore vecnum call @#putnum mov #sisr2,r1 ; "cr lf call @#puts + ; make ISR 100ms + mov #144,r0 ; 100 + call @#waitms +9$: mov (sp)+,r0 mov (sp)+,r1 mov (sp)+,r2 @@ -153,6 +196,28 @@ doisr: return + ; ---------------------- + ; prtlog - print log of isr vectors + ; word list from logbuf to logptr +prtlog: + tst @#logptr + beq 9$ ; ptr 0, slow mode, nothing logged + mov #logbuf,r2 +1$: + cmp r2,@#logptr + bhis 8$ ; end of list reached + mov (r2)+,r0 ; print vector from list + call @#putnum + mov #40,r0 ; print space separator + call @#putc + br 1$ +8$: + mov #scrlf,r1 ; CR/LF + call @#puts + +9$: + return + ; ---------------------- ; puts - print a string ; r1 = pointer, r0,r1 changed @@ -169,6 +234,10 @@ puts: numbf0: .blkw 10 ; space to mount number string numbf1 =. putnum: + mov r0,-(sp) + mov r1,-(sp) + mov r2,-(sp) + mov r3,-(sp) mov r0,r2 ; r2 = shifter mov #numbf1,r1 ; r1 = buffer pointer, backwards movb #0,-(r1) ; set terminating 0 @@ -181,12 +250,16 @@ putnum: add #60,r0 ; r0 += '0' movb r0,-(r1) ; write in buffer clc - ror r2 ; shift to next digit - ror r2 - ror r2 + asr r2 ; shift to next digit + asr r2 + asr r2 sob r3,1$ ; loop for all 6 digits call @#puts + mov (sp)+,r3 + mov (sp)+,r2 + mov (sp)+,r1 + mov (sp)+,r0 return @@ -206,11 +279,28 @@ putc: ; result in r0, r4 changed getc: mov #dladr,r4 ; set base addr -1$: tstb (r4) ; RCVR DONE? +1$: mov @#inchr,r0 ; external DEPOSIT into inchr? + bne 9$ ; yes: process as input + tstb (r4) ; else: RCVR DONE? bpl 1$ ; no, loop mov 2(r4),r0 ; return data +9$: + clr @#inchr return + ; ---------------------- + ; waitms - wait r0 milli seconds +waitms: + mov r1,-(sp) + +1$: ; -- outer loop + mov #764,r1 ; 500 +2$: ; -- inner loop: 1ms @ 1MHz + sob r1,2$ ; 1 cycle,2 us per loop (11/34) + + sob r0,1$ + mov (sp)+,r1 + return ; ---- Test strings, each 256 chars max --------- @@ -233,11 +323,25 @@ shello: .byte 15,12 ; CR, LF, .ascii /Chars 0..7 set the new processor priority level./ .byte 15,12 ; CR, LF, + .ascii /S = slow mode: ISR prints message (default)/ + .byte 15,12 ; CR, LF, + .ascii /F = fast mode: ISR logs vector, print and clr current log/ + .byte 15,12 ; CR, LF, + .byte 0 ; NUL=end marker + +sslwmd: + .ascii /Slow mode: called ISR prints vector directly/ + .byte 15,12 ; CR, LF, + .byte 0 ; NUL=end marker + +sfstmd: + .ascii /Fast mode: called ISR vectors are logged:/ + .byte 15,12 ; CR, LF, .byte 0 ; NUL=end marker sprio0: .byte 15,12 ; CR, LF, - .ascii /CPU priority now / + .ascii /CPU priority level now / .byte 0 sprio1: @@ -247,7 +351,16 @@ sprio1: sbye: .byte 15,12 .ascii /Good Bye!/ +scrlf: .byte 15,12,0 ; CR, LF, NUL=end marker + ; .byte 0 ; make addr even + + ; in "fast" mode, ISR calls fill this array with called vectors +logptr: + .word ; addr of next logentry to fill. 0 in "slow" mode + +logbuf: + .end diff --git a/10.02_devices/1_doc/DL11W/DL11-W Serial line Unit - Real-Time Clock Option Operator's Manual (May 1977, EK-DL11W-OP-001).pdf b/10.02_devices/1_doc/DL11W/DL11-W Serial line Unit - Real-Time Clock Option Operator's Manual (May 1977, EK-DL11W-OP-001).pdf new file mode 100644 index 0000000..730b434 Binary files /dev/null and b/10.02_devices/1_doc/DL11W/DL11-W Serial line Unit - Real-Time Clock Option Operator's Manual (May 1977, EK-DL11W-OP-001).pdf differ diff --git a/10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf b/10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf new file mode 100644 index 0000000..ba84d85 Binary files /dev/null and b/10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf differ diff --git a/10.02_devices/2_src/cpu.cpp b/10.02_devices/2_src/cpu.cpp index 2eb4a74..3664a7b 100644 --- a/10.02_devices/2_src/cpu.cpp +++ b/10.02_devices/2_src/cpu.cpp @@ -36,6 +36,11 @@ #include "unibusdevice.hpp" // definition of class device_c #include "cpu.hpp" +/* Adapter procs to Angelos CPU are not members of cpu_c calss + and need one global reference. + */ +static cpu_c *the_cpu = NULL; + cpu_c::cpu_c() : unibusdevice_c() // super class constructor { @@ -46,6 +51,9 @@ cpu_c::cpu_c() : default_base_addr = 0; // none default_intr_vector = 0; default_intr_level = 0; + priority_slot.value = 1 ; + + dma_request.set_priority_slot(priority_slot.value); // init parameters runmode.value = false; @@ -58,10 +66,13 @@ cpu_c::cpu_c() : memset(&bus, 0, sizeof(bus)); memset(&ka11, 0, sizeof(ka11)); ka11.bus = &bus; + + assert(the_cpu == NULL); // only one possible + the_cpu = this; // Singleton } cpu_c::~cpu_c() { - + the_cpu = NULL; } bool cpu_c::on_param_changed(parameter_c *param) { @@ -71,7 +82,7 @@ bool cpu_c::on_param_changed(parameter_c *param) { runmode.value = false; init.value = false; } - } + } return device_c::on_param_changed(param); // more actions (for enable) } @@ -79,34 +90,31 @@ extern "C" { // functions to be used by Angelos CPU emulator // Result: 1 = OK, 0 = bus timeout int cpu_dato(unsigned addr, unsigned data) { - bool timeout; - mailbox->dma.words[0] = data; - timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATO, addr, 1); - return !timeout; + uint16_t wordbuffer = (uint16_t) data; + unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATO, addr, &wordbuffer, 1); + return the_cpu->dma_request.success; } int cpu_datob(unsigned addr, unsigned data) { + uint16_t wordbuffer = (uint16_t) data; // TODO DATOB als 1 byte-DMA ! - bool timeout; - mailbox->dma.words[0] = data; - timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATOB, addr, 1); - return !timeout; + unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATOB, addr, &wordbuffer, 1); + return the_cpu->dma_request.success; } int cpu_dati(unsigned addr, unsigned *data) { - bool timeout; - - timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATI, addr, 1); - *data = mailbox->dma.words[0]; + uint16_t wordbuffer; + unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATI, addr, &wordbuffer, 1); + *data = wordbuffer; // printf("DATI; ba=%o, data=%o\n", addr, *data) ; - return !timeout; + return the_cpu->dma_request.success; } } // background worker. void cpu_c::worker(unsigned instance) { - UNUSED(instance) ; // only one + UNUSED(instance); // only one timeout_c timeout; bool nxm; unsigned pc = 0; diff --git a/10.02_devices/2_src/cpu.hpp b/10.02_devices/2_src/cpu.hpp index 11991aa..4ea43b0 100644 --- a/10.02_devices/2_src/cpu.hpp +++ b/10.02_devices/2_src/cpu.hpp @@ -46,6 +46,9 @@ public: cpu_c(); ~cpu_c(); + // CPU accesses memory actively + dma_request_c dma_request = dma_request_c(this); + bool on_param_changed(parameter_c *param) override; // must implement parameter_bool_c runmode = parameter_bool_c(this, "run", "r",/*readonly*/ diff --git a/10.02_devices/2_src/demo_io.cpp b/10.02_devices/2_src/demo_io.cpp index 3fa75e4..2aac192 100644 --- a/10.02_devices/2_src/demo_io.cpp +++ b/10.02_devices/2_src/demo_io.cpp @@ -46,7 +46,7 @@ demo_io_c::demo_io_c() : type_name.value = "demo_io_c"; log_label = "di"; - set_default_bus_params(0760100, 0, 0) ; // base addr, intr-vector, intr level + set_default_bus_params(0760100, 31, 0, 0); // base addr, intr-vector, intr level // init parameters switch_feedback.value = false; @@ -183,7 +183,7 @@ void demo_io_c::gpio_set_output(unsigned output_index, unsigned value) { // background worker. // udpate LEDS, poll switches direct to register flipflops void demo_io_c::worker(unsigned instance) { - UNUSED(instance) ; // only one + UNUSED(instance); // only one timeout_c timeout; while (!workers_terminate) { timeout.wait_ms(100); diff --git a/10.02_devices/2_src/demo_regs.cpp b/10.02_devices/2_src/demo_regs.cpp deleted file mode 100644 index cb212d4..0000000 --- a/10.02_devices/2_src/demo_regs.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* demo_regs.cpp: sample UNIBUS controller with register logic - - Copyright (c) 2018, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - 12-nov-2018 JH entered beta phase - - demo_regs: - a device to test the event and - implements 64 registers at start of IOpage - 0760000.. 0760076 - all registers are marked as "active": - DATI and DATO are routed via events into the controller logic - UNIBUS is stopped with long SSYN - - Registers have no functions, are simple memory cells. - */ -#include -#include -#include -//#include - -#include "utils.hpp" -#include "logger.hpp" -#include "unibus.h" -#include "unibusadapter.hpp" -#include "unibusdevice.hpp" // definition of class device_c -#include "demo_regs.hpp" - -demo_regs_c::demo_regs_c() : - unibusdevice_c() // super class constructor -{ - unsigned i; - - // static config - name.value = "DEMO_REGS"; - type_name.value = "demo_regs_c"; - log_label = "dr"; - - - set_default_bus_params(0760000, 0, 0) ; // base addr, intr-vector, intr level - - register_count = 32; // up to 760076 - // registers are "active": receive "on_after_register_access" - for (i = 0; i < this->register_count; i++) { - unibusdevice_register_t *reg = &(this->registers[i]); - sprintf(reg->name, "reg[0%03o]", i); // name is register offset - reg->active_on_dati = true; // controller state change on read - reg->active_on_dato = true; // writing changes controller state - reg->reset_value = 0; - reg->writable_bits = 0xffff; // all registers are memory cells - } - // dynamic state - access_count.value = 0; - -} - -bool demo_regs_c::on_param_changed(parameter_c *param) { - // no own parameter or "enable" logic - return unibusdevice_c::on_param_changed(param); // more actions (for enable) -} - -// background worker. -// Just print a heart beat -void demo_regs_c::worker(unsigned instance) { - UNUSED(instance) ; // only one - timeout_c timeout; - while (!workers_terminate) { - timeout.wait_ms(1000); - cout << "."; - } -} - -// process DATI/DATO access to one of my "active" registers -// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS. -// The time between PRU event and program flow into this callback -// is determined by ARM Linux context switch -// -// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc: -// do not read back dati_flipflops. -void demo_regs_c::on_after_register_access(unibusdevice_register_t *device_reg, - uint8_t unibus_control) { - - // emulate a plain memory cell: written values can be read unchanged - if (unibus_control == UNIBUS_CONTROL_DATI) { - } - - if (unibus_control == UNIBUS_CONTROL_DATO) - set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); - // this is also called for some DATIs, no action anyhow. - - access_count.value++; - // DEBUG writes to disk & console ... measured delay up to 30ms ! - //DEBUG(LL_DEBUG, LC_demo_regs, "[%6u] reg +%d @ %06o %s", accesscount, (int ) device_reg->index, - // device_reg->addr, unibus_c::control2text(unibus_control)); -} - -void demo_regs_c::on_power_changed(void) { - if (power_down) { // power-on defaults - } -} - -// UNIBUS INIT: clear all registers -void demo_regs_c::on_init_changed(void) { - // write all registers to "reset-values" - if (init_asserted) { - reset_unibus_registers(); - INFO("demo_regs_c::on_init()"); - } -} - diff --git a/10.02_devices/2_src/dl11w.cpp b/10.02_devices/2_src/dl11w.cpp index ff5ac1b..e112fb3 100644 --- a/10.02_devices/2_src/dl11w.cpp +++ b/10.02_devices/2_src/dl11w.cpp @@ -41,6 +41,7 @@ #include "utils.hpp" #include "gpios.hpp" +#include "logsource.hpp" #include "unibusadapter.hpp" #include "unibusdevice.hpp" // definition of class device_c #include "unibus.h" @@ -50,7 +51,8 @@ //------------------------------------------------- -slu_c::slu_c() : unibusdevice_c() { +slu_c::slu_c() : + unibusdevice_c() { set_workers_count(2); // receiver and transmitte have own threads //ip_host.value = IP_HOST; // not used @@ -61,11 +63,19 @@ slu_c::slu_c() : unibusdevice_c() { type_name.value = "slu_c"; log_label = "slu"; - break_enable.value = 1 ; // SW4-1 default ON - error_bits_enable.value = 1 ; // SE4-7 default ON + break_enable.value = 1; // SW4-1 default ON + error_bits_enable.value = 1; // SE4-7 default ON // SLU has 2 Interrupt vectors: base = RCV, base+= XMT - set_default_bus_params(SLU_ADDR, SLU_VECTOR, SLU_LEVEL); // base addr, intr-vector, intr level + // put in slot 1, closest to CPU + set_default_bus_params(SLU_ADDR, SLU_SLOT, SLU_VECTOR, SLU_LEVEL); // base addr, intr-vector, intr level + rcvintr_request.set_priority_slot(default_priority_slot); + rcvintr_request.set_level(default_intr_level); + rcvintr_request.set_vector(default_intr_vector); + // XMT INTR: lower priority->netx slot, and next vector + xmtintr_request.set_priority_slot(default_priority_slot + 1); + xmtintr_request.set_level(default_intr_level); + xmtintr_request.set_vector(default_intr_vector + 4); // init parameters @@ -126,7 +136,9 @@ bool slu_c::on_param_changed(parameter_c *param) { mode.readonly = true; INFO("Serial port %s opened", serialport.value.c_str()); - rs232.cputs("Serial port opened\n\r"); + char buff[256] ; + sprintf(buff, "Serial port %s opened\n\r", serialport.value.c_str()); + rs232.cputs(buff); } else { // disable SLU rs232.CloseComport(); @@ -142,66 +154,95 @@ bool slu_c::on_param_changed(parameter_c *param) { //-------------------------------------------- -// Update RCSR -void slu_c::set_rcsr_dati_value(void) { +// calc static INTR condition level. +// Change of that condition calculated by intr_request_c.is_condition_raised() +bool slu_c::get_rcv_intr_level(void) { + return rcv_done && rcv_intr_enable; +} + +// Update RCSR and optionally generate INTR +void slu_c::set_rcsr_dati_value_and_INTR(void) { uint16_t val = (rcv_active ? RCSR_RCVR_ACT : 0) | (rcv_done ? RCSR_RCVR_DONE : 0) | (rcv_intr_enable ? RCSR_RCVR_INT_ENB : 0); - set_register_dati_value(reg_rcsr, val, __func__); + switch (rcvintr_request.edge_detect(get_rcv_intr_level())) { + case intr_request_c::INTERRUPT_EDGE_RAISING: + // set register atomically with INTR, if INTR not blocked + unibusadapter->INTR(rcvintr_request, reg_rcsr, val); + break; + case intr_request_c::INTERRUPT_EDGE_FALLING: + // BR4 is tied to monitor and enable, so raised INTRs may get canceled + unibusadapter->cancel_INTR(rcvintr_request); + set_register_dati_value(reg_rcsr, val, __func__); + break; + default: + set_register_dati_value(reg_rcsr, val, __func__); + } } // PDP-11 writes into RCSR void slu_c::eval_rcsr_dato_value(void) { uint16_t val = get_register_dato_value(reg_rcsr); - bool old_rdr_enab = rcv_rdr_enb; - bool old_intr = rcv_done && rcv_intr_enable; rcv_intr_enable = val & RCSR_RCVR_INT_ENB ? 1 : 0; rcv_rdr_enb = val & RCSR_RDR_ENB ? 1 : 0; - if (!old_rdr_enab && rcv_rdr_enb) + if (rcv_rdr_enb) rcv_done = 0; // raising edge clears rcv_done // if rcvr_done and int enable goes high: INTR - bool new_intr = rcv_done && rcv_intr_enable; - if (!old_intr && new_intr) // raising edge - interrupt(intr_vector.value, intr_level.value); + set_rcsr_dati_value_and_INTR(); } // Update RBUF, readonly void slu_c::set_rbuf_dati_value(void) { - uint16_t val = 0 ; + uint16_t val = 0; if (error_bits_enable.value) { - val = (rcv_or_err ? RBUF_OR_ERR : 0) | (rcv_fr_err ? RBUF_FR_ERR : 0) - | (rcv_p_err ? RBUF_P_ERR : 0); - if (val) // set general error flag - val |= RBUF_ERROR; + val = (rcv_or_err ? RBUF_OR_ERR : 0) | (rcv_fr_err ? RBUF_FR_ERR : 0) + | (rcv_p_err ? RBUF_P_ERR : 0); + if (val) // set general error flag + val |= RBUF_ERROR; } val |= rcv_buffer; // received char in bits 7..0 set_register_dati_value(reg_rbuf, val, __func__); } -// Update Transmit Status Register XCSR -void slu_c::set_xcsr_dati_value(void) { +bool slu_c::get_xmt_intr_level() { + return xmt_ready && xmt_intr_enable; +} + +// Update Transmit Status Register XCSR and optionally generate INTR +void slu_c::set_xcsr_dati_value_and_INTR(void) { uint16_t val = (xmt_ready ? XCSR_XMIT_RDY : 0) | (xmt_intr_enable ? XCSR_XMIT_INT_ENB : 0) | (xmt_maint ? XCSR_MAINT : 0) | (xmt_break ? XCSR_BREAK : 0); - set_register_dati_value(reg_xcsr, val, __func__); + switch (xmtintr_request.edge_detect(get_xmt_intr_level())) { + case intr_request_c::INTERRUPT_EDGE_RAISING: + // set register atomically with INTR, if INTR not blocked + unibusadapter->INTR(xmtintr_request, reg_xcsr, val); + break; + case intr_request_c::INTERRUPT_EDGE_FALLING: + // BR4 is tied to monitor and enable, so raised INTRs may get canceled + unibusadapter->cancel_INTR(xmtintr_request); + set_register_dati_value(reg_xcsr, val, __func__); + break; + default: + set_register_dati_value(reg_xcsr, val, __func__); + } + } void slu_c::eval_xcsr_dato_value(void) { uint16_t val = get_register_dato_value(reg_xcsr); - bool old_intr = xmt_ready && xmt_intr_enable; bool old_break = xmt_break; - xmt_intr_enable = val & XCSR_XMIT_INT_ENB ? 1 : 0; xmt_maint = val & XCSR_MAINT ? 1 : 0; xmt_break = val & XCSR_BREAK ? 1 : 0; // if xmt_ready and int enable goes high: INTR - bool new_intr = xmt_ready && xmt_intr_enable; - if (!old_intr && new_intr) // raising edge - interrupt(intr_vector.value + 4, intr_level.value); + set_xcsr_dati_value_and_INTR(); + if (old_break != xmt_break) { // re-evaluate break state on bit change if (break_enable.value) rs232.SetBreak(xmt_break); - else rs232.SetBreak(0); + else + rs232.SetBreak(0); } } @@ -210,7 +251,6 @@ void slu_c::eval_xbuf_dato_value(void) { xmt_buffer = get_register_dato_value(reg_xbuf) & 0xff; } - // process DATI/DATO access to one of my "active" registers // !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS. // The time between PRU event and program flow into this callback @@ -224,13 +264,16 @@ void slu_c::on_after_register_access(unibusdevice_register_t *device_reg, // if (unibus_control == UNIBUS_CONTROL_DATO) // bus write // set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); + if (unibusadapter->line_INIT) + return; // do nothing wile reset + switch (device_reg->index) { case slu_idx_rcsr: if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write into RCSR pthread_mutex_lock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses eval_rcsr_dato_value(); // may generate INTR - set_rcsr_dati_value(); + set_rcsr_dati_value_and_INTR(); // ignore reader enable pthread_mutex_unlock(&on_after_rcv_register_access_mutex); } @@ -239,7 +282,7 @@ void slu_c::on_after_register_access(unibusdevice_register_t *device_reg, // signal data has been read from bus pthread_mutex_lock(&on_after_rcv_register_access_mutex); rcv_done = 0; - set_rcsr_dati_value(); + set_rcsr_dati_value_and_INTR(); pthread_mutex_unlock(&on_after_rcv_register_access_mutex); } break; @@ -247,7 +290,7 @@ void slu_c::on_after_register_access(unibusdevice_register_t *device_reg, if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write pthread_mutex_lock(&on_after_xmt_register_access_mutex); eval_xcsr_dato_value(); // may trigger INTR - set_xcsr_dati_value(); + set_xcsr_dati_value_and_INTR(); pthread_mutex_unlock(&on_after_xmt_register_access_mutex); } break; @@ -256,7 +299,7 @@ void slu_c::on_after_register_access(unibusdevice_register_t *device_reg, pthread_mutex_lock(&on_after_xmt_register_access_mutex); eval_xbuf_dato_value(); xmt_ready = 0; // signal worker: xmt_data pending - set_xcsr_dati_value(); + set_xcsr_dati_value_and_INTR(); // on_after_register_access_cond used for xmt worker pthread_cond_signal(&on_after_xmt_register_access_cond); pthread_mutex_unlock(&on_after_xmt_register_access_mutex); @@ -289,7 +332,9 @@ void slu_c::on_init_changed(void) { xmt_intr_enable = 0; xmt_maint = 0; xmt_break = 0; - INFO("slu_c::on_init()"); + rcvintr_request.edge_detect_reset(); + xmtintr_request.edge_detect_reset(); + // INFO("slu_c::on_init()"); } } @@ -303,18 +348,20 @@ void slu_c::worker_rcv(void) { //unsigned poll_periods_us = 1000000 / baudrate.value; /* Receiver not time critical? UARTS are buffering - So if thread is swapped out and back a burst of characters appear. - -> Wait after each character for transfer time before polling - RS232 again. - */ - unsigned poll_periods_us = (rs232.CharTransmissionTime_us * 9) / 10; + So if thread is swapped out and back a burst of characters appear. + -> Wait after each character for transfer time before polling + RS232 again. + */ + unsigned poll_periods_us = (rs232.CharTransmissionTime_us * 9) / 10; // poll a bit faster to be ahead of char stream. // don't oversample: PDP-11 must process char in that time - + // worker_init_realtime_priority(rt_device); while (!workers_terminate) { timeout.wait_us(poll_periods_us); + if (unibusadapter->line_INIT) + continue; // do nothing while reset // "query // rcv_active: can only be set by polling the UART input GPIO pin? // at the moments, it is only sent on maintenance loopback xmt @@ -346,15 +393,10 @@ void slu_c::worker_rcv(void) { } else // received non escaped data byte rcv_buffer = buffer[0]; - bool old_rcvdone = rcv_done ; rcv_done = 1; - rcv_active = 0 ; + rcv_active = 0; set_rbuf_dati_value(); - set_rcsr_dati_value(); // INTR! - if (old_rcvdone == 0 && rcv_done == 1 && rcv_intr_enable) - interrupt(intr_vector.value, intr_level.value); - - + set_rcsr_dati_value_and_INTR(); // INTR! pthread_mutex_unlock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses } } @@ -367,10 +409,11 @@ void slu_c::worker_xmt(void) { // Transmitter not time critical // worker_init_realtime_priority(rt_device); - + while (!workers_terminate) { // 1. wait for xmt signal - int res = pthread_cond_wait(&on_after_xmt_register_access_cond, &on_after_xmt_register_access_mutex); + int res = pthread_cond_wait(&on_after_xmt_register_access_cond, + &on_after_xmt_register_access_mutex); // on_after_xmt_register_access_mutex remains locked all the time if (res != 0) { ERROR("SLU::worker_xmt() pthread_cond_wait = %d = %s>", res, strerror(res)); @@ -380,11 +423,11 @@ void slu_c::worker_xmt(void) { // 2. transmit rs232.SendByte(xmt_buffer); xmt_ready = 0; - set_xcsr_dati_value(); + set_xcsr_dati_value_and_INTR(); if (xmt_maint) { // loop back: simulate data byte coming in pthread_mutex_lock(&on_after_rcv_register_access_mutex); rcv_active = 1; - set_rcsr_dati_value(); + set_rcsr_dati_value_and_INTR(); pthread_mutex_unlock(&on_after_rcv_register_access_mutex); } @@ -395,11 +438,9 @@ void slu_c::worker_xmt(void) { if (xmt_maint) // put sent byte into rcv buffer, receiver will poll it rs232.LoopbackByte(xmt_buffer); - bool old_xmt_ready = xmt_ready ; xmt_ready = 1; - set_xcsr_dati_value(); - if (old_xmt_ready == 0 && xmt_ready == 1 && xmt_intr_enable) - interrupt(intr_vector.value + 4, intr_level.value); + set_xcsr_dati_value_and_INTR(); + // has rcv or xmt interrupt priority on maintennace loop back } assert(!pthread_mutex_unlock(&on_after_xmt_register_access_mutex)); @@ -424,21 +465,28 @@ ltc_c::ltc_c() : name.value = "KW11"; type_name.value = "ltc_c"; log_label = "ltc"; + // slot = 3: + set_default_bus_params(LTC_ADDR, LTC_SLOT, LTC_VECTOR, LTC_LEVEL); // base addr, intr-vector, intr level + intr_request.set_priority_slot(default_priority_slot); + intr_request.set_level(default_intr_level); + intr_request.set_vector(default_intr_vector); - set_default_bus_params(LTC_ADDR, LTC_VECTOR, LTC_LEVEL); // base addr, intr-vector, intr level - - // init parameters - - // controller has some register - register_count = ltc_idx_count; - - reg_lks = &(this->registers[ltc_idx_lks]); // @ base addr + // controller has only one register + register_count = 1; + reg_lks = &(this->registers[0]); // @ base addr strcpy(reg_lks->name, "LKS"); // Line Clock Status Register - reg_lks->active_on_dati = true; // no controller state change + reg_lks->active_on_dati = false; // status polled by CPU, not active reg_lks->active_on_dato = true; reg_lks->reset_value = 0; - reg_lks->writable_bits = 0xff; + reg_lks->writable_bits = LKS_INT_ENB; // interrupt enable + // init parameters + frequency.value = 50; + ltc_enable.value = true; + + // init controller state + intr_enable = 0; + intr_monitor = 0; } ltc_c::~ltc_c() { @@ -446,56 +494,61 @@ ltc_c::~ltc_c() { bool ltc_c::on_param_changed(parameter_c *param) { // no own parameter or "enable" logic here - return unibusdevice_c::on_param_changed(param); // more actions (for enable) + if (param == &frequency) { + // accept only these + return (frequency.new_value == 50 || frequency.new_value == 60); + } else + return unibusdevice_c::on_param_changed(param); // more actions (for enable) } -// background worker. -void ltc_c::worker(unsigned instance) { - UNUSED(instance); // only one - timeout_c timeout; - while (!workers_terminate) { - if (ltc_input.value) { - //should really wait for LTC input trailing edge here - timeout.wait_ms(10000); - } else - timeout.wait_ms(LTC_MSRATE_MS); -#if 0 - printf("[%o",buslatches_getval[0]); -#endif - if (lke.value) { - lks.value |= LKS_INT_MON; - set_register_dati_value(reg_lks, lks.value, __func__); - } +// calc static INTR condition level. +// Change of that condition calculated by intr_request_c.is_condition_raised() +// on raising edge. +bool ltc_c::get_intr_signal_level() { + return intr_monitor && intr_enable; +} +// set status register, and optionally generate INTR +// intr_raise: if inactive->active transition of interrupt condition detected. +void ltc_c::set_lks_dati_value_and_INTR(void) { + uint16_t val = (intr_monitor ? LKS_INT_MON : 0) | (intr_enable ? LKS_INT_ENB : 0); + switch (intr_request.edge_detect(get_intr_signal_level())) { + case intr_request_c::INTERRUPT_EDGE_RAISING: + // set register atomically with INTR, if INTR not blocked + unibusadapter->INTR(intr_request, reg_lks, val); + break; + case intr_request_c::INTERRUPT_EDGE_FALLING: + // BR6 is tied to monitor and enable, so raised INTRs may get canceled + unibusadapter->cancel_INTR(intr_request); + set_register_dati_value(reg_lks, val, __func__); + break; + default: + set_register_dati_value(reg_lks, val, __func__); } - } // process DATI/DATO access to one of my "active" registers void ltc_c::on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control) { + pthread_mutex_lock(&on_after_register_access_mutex); if (unibus_control == UNIBUS_CONTROL_DATO) // bus write set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); switch (device_reg->index) { - case ltc_idx_lks: - if (unibus_control == UNIBUS_CONTROL_DATI) { // bus read - // signal data has been read from bus - lks.value &= ~ LKS_INT_MON; - set_register_dati_value(reg_lks, lks.value, __func__); - } + case 0: // LKS if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write - lks.value = reg_lks->active_dato_flipflops; - ltc_interrupt_enable.value = !!(reg_lks->active_dato_flipflops & (LKS_INT_ENB)); - interrupt(intr_vector.value, intr_level.value); + intr_enable = !!(reg_lks->active_dato_flipflops & LKS_INT_ENB); + intr_monitor = !!(reg_lks->active_dato_flipflops & LKS_INT_MON); + set_lks_dati_value_and_INTR(); } break; default: break; } + pthread_mutex_unlock(&on_after_register_access_mutex); } @@ -509,7 +562,73 @@ void ltc_c::on_init_changed(void) { // write all registers to "reset-values" if (init_asserted) { reset_unibus_registers(); - INFO("ltc_c::on_init()"); + intr_enable = 0; + intr_monitor = 1; + + intr_request.edge_detect_reset(); + // INFO("ltc_c::on_init()"); + } +} + +/* background worker. + Freqnecy of clock signal edges is tied to system time, not to "wait" periods + This worker may get delayed arbitray amount of time (as every thread), + lost edges are compensated + */ +void ltc_c::worker(unsigned instance) { + UNUSED(instance); // only one + timeout_c global_time; + timeout_c timeout; + int64_t global_next_edge_ns; + + INFO("KW11 time resolution is < %u us", + (unsigned )(global_time.get_resolution_ns() / 1000)); + global_time.start(0); + global_next_edge_ns = global_time.elapsed_ns(); + uint64_t global_edge_count = 0; + while (!workers_terminate) { + + // signal egde period may change if 50/60 Hz is changed + uint64_t edge_period_ns = BILLION / (2 * frequency.value); + uint64_t wait_ns; + // overdue_ns: time which signal edge is too late + int64_t overdue_ns = (int64_t) global_time.elapsed_ns() - global_next_edge_ns; + // INFO does not work on 64 ints + // printf("elapsed [ms] =%u, overdue [us] =%u\n", (unsigned) global_time.elapsed_ms(), (unsigned) overdue_ns/1000) ; + // if overdue_ns positive, next signal edge should have occured + if (overdue_ns < 0) { + wait_ns = -overdue_ns; // wait until next edge time reached + } else { + // time for next signal edge reached + if (ltc_enable.value) { + global_edge_count++; + clock_signal = !clock_signal; // square wave + if (clock_signal) { + intr_monitor = 1; + pthread_mutex_lock(&on_after_register_access_mutex); + set_lks_dati_value_and_INTR(); + pthread_mutex_unlock(&on_after_register_access_mutex); + } + } else + // clock disconnected + clock_signal = 0; + + // time of next signal edge + global_next_edge_ns += edge_period_ns; + // overdue_ns now time which next signal edge is too late + overdue_ns -= edge_period_ns; + + if (overdue_ns < 0) + // next edge now in future: wait exact + wait_ns = -overdue_ns; + else + // next edge still in past: + // wait shorter than signal edge period to keep up slowly + wait_ns = edge_period_ns / 2; + //if ((global_edge_count % 100) == 0) + // INFO("LTC: %u secs by edges", (unsigned)(global_edge_count/100) ) ; + } + timeout.wait_ns(wait_ns); } } diff --git a/10.02_devices/2_src/dl11w.hpp b/10.02_devices/2_src/dl11w.hpp index 3b50efe..8e1fb04 100644 --- a/10.02_devices/2_src/dl11w.hpp +++ b/10.02_devices/2_src/dl11w.hpp @@ -44,20 +44,22 @@ using namespace std; #define DL11A 1 #if DL11A // console (teletype keyboard & printer) #define SLU_ADDR 0777560 +#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT #define SLU_LEVEL 04 #define SLU_VECTOR 060 // RCV +0, XMT +4 #elif DL11B // paper tape punch and reader +#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT #define SLU_ADDR 0777550 #define SLU_LEVEL 04 #define SLU_VECTOR 070 #else // other serial device #define SLU_ADDR 0776500 +#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT #define SLU_LEVEL 04 #define SLU_VECTOR 0300 #endif -//#define LTC_ADDR 0777546 -// moved here to avoid clash with physical LTC installed -#define LTC_ADDR 0777544 +#define LTC_ADDR 0777546 +#define LTC_SLOT (SLU_SLOT+2) #define LTC_LEVEL 06 #define LTC_VECTOR 0100 @@ -92,10 +94,6 @@ enum slu_reg_index { slu_idx_rcsr = 0, slu_idx_rbuf, slu_idx_xcsr, slu_idx_xbuf, slu_idx_count, }; -enum ltc_reg_index { - ltc_idx_lks = 0, ltc_idx_count, -}; - // ------------------------------------------ SLU ----------------------------- class slu_c: public unibusdevice_c { private: @@ -106,6 +104,10 @@ private: unibusdevice_register_t *reg_xcsr; unibusdevice_register_t *reg_xbuf; + // two interrupts of same level, need slot and slot+1 + intr_request_c rcvintr_request = intr_request_c(this); + intr_request_c xmtintr_request = intr_request_c(this); + /*** SLU is infact 2 independend devices: RCV and XMT ***/ pthread_cond_t on_after_rcv_register_access_cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t on_after_rcv_register_access_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -120,8 +122,7 @@ private: uint8_t rcv_buffer;bool rcv_rdr_enb; // reader enable. Cleared by receive or init pthread_cond_t on_after_xmt_register_access_cond = PTHREAD_COND_INITIALIZER; - pthread_mutex_t on_after_xmt_register_access_mutex = PTHREAD_MUTEX_INITIALIZER; - bool xmt_ready;// transmitter ready. INTR, cleared on XBUF access + pthread_mutex_t on_after_xmt_register_access_mutex = PTHREAD_MUTEX_INITIALIZER;bool xmt_ready; // transmitter ready. INTR, cleared on XBUF access bool xmt_intr_enable; // receiver interrupt enabled bool xmt_maint; // set 1 for local loop back @@ -129,10 +130,12 @@ private: uint8_t xmt_buffer; // convert between register ansd state variables - void set_rcsr_dati_value(void); + bool get_rcv_intr_level(void);bool get_xmt_intr_level(void); + + void set_rcsr_dati_value_and_INTR(void); void eval_rcsr_dato_value(void); void set_rbuf_dati_value(void); - void set_xcsr_dati_value(void); + void set_xcsr_dati_value_and_INTR(void); void eval_xcsr_dato_value(void); void eval_xbuf_dato_value(void); @@ -144,7 +147,7 @@ public: //parameter_string_c ip_host = parameter_string_c( this, "SLU socket IP host", "host", /*readonly*/ false, "ip hostname"); //parameter_unsigned_c ip_port = parameter_unsigned_c(this, "SLU socket IP serialport", "serialport", /*readonly*/ false, "", "%d", "ip serialport", 32, 10); parameter_string_c serialport = parameter_string_c(this, "serialport", "p", /*readonly*/ - false, "Linux serial port: \"ttyS1\" or \"ttyS2\""); + false, "Linux serial port: \"ttyS1\" or \"ttyS2\""); parameter_unsigned_c baudrate = parameter_unsigned_c(this, "baudrate", "b", /*readonly*/ false, "", "%d", "Baudrate: 110, 300, ... 38400", 38400, 10); @@ -153,8 +156,8 @@ public: parameter_string_c mode = parameter_string_c(this, "mode", "m", /*readonly*/false, "Mode: 8N1, 7E1, ... "); - parameter_bool_c error_bits_enable = parameter_bool_c(this, "errorbits", "eb", /*readonly*/false, - "Enable error bits (M7856 SW4-7)"); + parameter_bool_c error_bits_enable = parameter_bool_c(this, "errorbits", "eb", /*readonly*/ + false, "Enable error bits (M7856 SW4-7)"); parameter_bool_c break_enable = parameter_bool_c(this, "break", "b", /*readonly*/false, "Enable BREAK transmission (M7856 SW4-1)"); @@ -200,19 +203,25 @@ private: unibusdevice_register_t *reg_lks; + // KW11 has one interrupt + intr_request_c intr_request = intr_request_c(this); + + bool clock_signal; // value of 50Hz square wave signal + bool intr_enable; // interrupt enable, LKS bit 6 + bool intr_monitor ; // LKS bit 7 + + bool get_intr_signal_level(void); + void set_lks_dati_value_and_INTR(void); + public: ltc_c(); ~ltc_c(); - parameter_unsigned_c lks = parameter_unsigned_c(this, "Line Clock Status Register", "lks", /*readonly*/ - false, "", "%o", "Internal state", 32, 8); - parameter_bool_c lke = parameter_bool_c(this, "LKS timer enable", "lke",/*readonly*/false, - "1 = enable update of LKS_IMON by timer"); - parameter_bool_c ltc_input = parameter_bool_c(this, "LTC input enable", "ltc",/*readonly*/ - false, "1 = enable update of LKS_IMON by LTC Input"); - parameter_bool_c ltc_interrupt_enable = parameter_bool_c(this, "LTC interrupt enable", - "lie",/*readonly*/false, "1 = enable interrupt"); + parameter_unsigned_c frequency = parameter_unsigned_c(this, "Line clock frequency", "freq", /*readonly*/ + false, "", "%d", "50/60 Hz", 32, 10); + parameter_bool_c ltc_enable = parameter_bool_c(this, "LTC input enable", "ltc",/*readonly*/ + false, "1 = enable update of LKS by LTC Input"); // background worker function void worker(unsigned instance) override; @@ -224,10 +233,6 @@ public: bool on_param_changed(parameter_c *param) override; // must implement void on_power_changed(void) override; void on_init_changed(void) override; - /* - void change_state(unsigned new_state); - void do_command_done(void); - */ }; #endif diff --git a/10.02_devices/2_src/mscp_drive.hpp b/10.02_devices/2_src/mscp_drive.hpp index 6754a6a..1b7200a 100644 --- a/10.02_devices/2_src/mscp_drive.hpp +++ b/10.02_devices/2_src/mscp_drive.hpp @@ -10,6 +10,7 @@ #include #include +#include // unique_ptr #include "parameter.hpp" #include "storagedrive.hpp" @@ -91,7 +92,8 @@ private: void UpdateMetadata(void); DriveInfo _driveInfo;bool _online; uint32_t _unitDeviceNumber; - uint16_t _unitClassModel;bool _useImageSize; + uint16_t _unitClassModel; + bool _useImageSize; // // RCT ("Replacement and Caching Table") data: diff --git a/10.02_devices/2_src/panel.cpp b/10.02_devices/2_src/panel.cpp index e12839e..d79dee8 100644 --- a/10.02_devices/2_src/panel.cpp +++ b/10.02_devices/2_src/panel.cpp @@ -439,7 +439,7 @@ void paneldriver_c::i2c_sync_all_params() { Update controls and parameters */ void paneldriver_c::worker(unsigned instance) { - UNUSED(instance) ; // only one + UNUSED(instance); // only one timeout_c timeout; while (!workers_terminate) { diff --git a/10.02_devices/2_src/rk11.cpp b/10.02_devices/2_src/rk11.cpp index 1f1173f..de41330 100755 --- a/10.02_devices/2_src/rk11.cpp +++ b/10.02_devices/2_src/rk11.cpp @@ -25,8 +25,13 @@ rk11_c::rk11_c() : name.value = "rk"; type_name.value = "RK11"; log_label = "rk"; - - set_default_bus_params(0777400, 0220, 5) ; // base addr, intr-vector, intr level + + // base addr, intr-vector, intr level + set_default_bus_params(0777400, 10, 0220, 5) ; + dma_request.set_priority_slot(default_priority_slot) ; + intr_request.set_priority_slot(default_priority_slot) ; + intr_request.set_level(default_intr_level) ; + intr_request.set_vector(default_intr_vector) ; // The RK11 controller has seven registers, // We allocate 8 because one address in the address space is unused. @@ -139,23 +144,24 @@ void rk11_c::dma_transfer(DMARequest &request) { // Write FROM buffer TO unibus memory, IBA on: // We only need to write the last word in the buffer to memory. - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, request.address, request.buffer + request.count - 1, - 1, NULL); + 1); + request.timeout = !dma_request.success ; } else { // Read FROM unibus memory TO buffer, IBA on: // We read a single word from the unibus and fill the // entire buffer with this value. - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, request.address, request.buffer, - 1, - NULL); + 1); + request.timeout = !dma_request.success ; } } else @@ -164,22 +170,22 @@ void rk11_c::dma_transfer(DMARequest &request) if (request.write) { // Write FROM buffer TO unibus memory - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, request.address, request.buffer, - request.count, - NULL); + request.count); + request.timeout = !dma_request.success ; } else { // Read FROM unibus memory TO buffer - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, request.address, request.buffer, - request.count, - NULL); + request.count); + request.timeout = !dma_request.success ; } } @@ -960,7 +966,7 @@ void rk11_c::invoke_interrupt(void) // if (_ide) { - interrupt(); + unibusadapter->INTR(intr_request, NULL, 0); // todo: link to interupt register } } diff --git a/10.02_devices/2_src/rk11.hpp b/10.02_devices/2_src/rk11.hpp index 779190d..1dea632 100755 --- a/10.02_devices/2_src/rk11.hpp +++ b/10.02_devices/2_src/rk11.hpp @@ -14,6 +14,7 @@ using namespace std; #include "utils.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" #include "storagecontroller.hpp" #include "rk05.hpp" @@ -149,6 +150,12 @@ private: Worker_Finish = 2, } _worker_state; + // Unibusadapter: RK11 has one INTR and DMA + // should be merged with RK11::DMARequest + dma_request_c dma_request = dma_request_c(this) ; // operated by unibusadapter + intr_request_c intr_request = intr_request_c(this) ; + + public: rk11_c(); diff --git a/10.02_devices/2_src/rl0102.cpp b/10.02_devices/2_src/rl0102.cpp index c39aac4..111f220 100644 --- a/10.02_devices/2_src/rl0102.cpp +++ b/10.02_devices/2_src/rl0102.cpp @@ -634,7 +634,7 @@ bool RL0102_c::cmd_write_next_sector_data(uint16_t *buffer, unsigned buffer_size // thread void RL0102_c::worker(unsigned instance) { - UNUSED(instance) ; // only one + UNUSED(instance); // only one timeout_c timeout; // set prio to RT, but less than RL11 controller diff --git a/10.02_devices/2_src/rl0102.hpp b/10.02_devices/2_src/rl0102.hpp index a2920ea..f8e1b88 100644 --- a/10.02_devices/2_src/rl0102.hpp +++ b/10.02_devices/2_src/rl0102.hpp @@ -121,7 +121,7 @@ public: volatile uint16_t status_word; // visible to controller parameter_unsigned_c rotation_umin = parameter_unsigned_c(this, "rotation", "rot",/*readonly*/ - true, "rpm", "%d", "Current speed of disk", 32, 10); + true, "rpm", "%d", "Current speed of disk", 32, 10); // RL0102_STATE_*. no enum, is param parameter_unsigned_c state = parameter_unsigned_c(this, "state", "st", /*readonly*/ true, "", "%d", "Internal state", 32, 10); @@ -138,9 +138,9 @@ public: parameter_bool_c fault_lamp = parameter_bool_c(this, "faultlamp", "fl", /*readonly*/ true, "State of FAULT lamp"); parameter_bool_c writeprotect_lamp = parameter_bool_c(this, "writeprotectlamp", "wpl", /*readonly*/ - true, "State of WRITE PROTECT lamp"); + true, "State of WRITE PROTECT lamp"); parameter_bool_c writeprotect_button = parameter_bool_c(this, "writeprotectbutton", "wpb", /*readonly*/ - false, "Writeprotect button pressed"); + false, "Writeprotect button pressed"); // cover normally always "closed", need to get opened for ZRLI parameter_bool_c cover_open = parameter_bool_c(this, "coveropen", "co", /*readonly*/ diff --git a/10.02_devices/2_src/rl11.cpp b/10.02_devices/2_src/rl11.cpp index 1b06221..fbbe383 100644 --- a/10.02_devices/2_src/rl11.cpp +++ b/10.02_devices/2_src/rl11.cpp @@ -136,7 +136,12 @@ RL11_c::RL11_c(void) : type_name.value = "RL11"; log_label = "rl"; - set_default_bus_params(0774400, 0160, 5) ; // base addr, intr-vector, intr level + // base addr, intr-vector, intr level + set_default_bus_params(0774400, 15, 0160, 5); + dma_request.set_priority_slot(default_priority_slot); + intr_request.set_priority_slot(default_priority_slot); + intr_request.set_level(default_intr_level); + intr_request.set_vector(default_intr_vector); // add 4 RL disk drives drivecount = 4; @@ -201,7 +206,7 @@ bool RL11_c::on_param_changed(parameter_c *param) { // disabled disconnect_from_panel(); } - } + } return storagecontroller_c::on_param_changed(param); // more actions (for enable) } @@ -506,16 +511,12 @@ void RL11_c::do_command_done(void) { * RDY interrupt too late and at wrong program position */ // worker_boost_realtime_priority(); -// SET_DEBUG_PIN1(1) - ; - change_state(RL11_STATE_CONTROLLER_READY); // scheduler may inject time here, if called from low prio worker() ! // pending interrupt triggered - interrupt(); + //TODO: connect to itnerrupt register busreg_CS + unibusadapter->INTR(intr_request, NULL, 0); DEBUG("Interrupt!"); -// SET_DEBUG_PIN1(0) - ; // worker_restore_realtime_priority(); } else // no intr @@ -723,26 +724,32 @@ void RL11_c::state_readwrite() { //logger.debug_hexdump(LC_RL, "Read data between disk access and DMA", // (uint8_t *) silo, sizeof(silo), NULL); // start DMA transmission of SILO into memory - error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATO, - unibus_address, silo, dma_wordcount, &unibus_address); + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, unibus_address, silo, + dma_wordcount); + error_dma_timeout = !dma_request.success; + unibus_address = dma_request.unibus_end_addr; } else if (function_code == CMD_WRITE_CHECK) { // read sector data to compare with sector data drive->cmd_read_next_sector_data(silo, 128); // logger.debug_hexdump(LC_RL, "Read data between disk access and DMA", // (uint8_t *) silo, sizeof(silo), NULL); // start DMA transmission of memory to compare with SILO - error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI, - unibus_address, silo_compare, dma_wordcount, &unibus_address); + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, unibus_address, + silo_compare, dma_wordcount); + error_dma_timeout = !dma_request.success; + unibus_address = dma_request.unibus_end_addr; } else if (function_code == CMD_WRITE_DATA) { // start DMA transmission of memory into SILO - error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI, - unibus_address, silo, dma_wordcount, &unibus_address); + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, unibus_address, silo, + dma_wordcount); + error_dma_timeout = !dma_request.success; + unibus_address = dma_request.unibus_end_addr; } // request_client_DMA() was blocking, DMA processed now. // unibus_address updated to last accesses address unibus_address += 2; // was last address, is now next to fill - // if timeout: addr AFTER illegal address (verified) + // if timeout: yes, current addr is addr AFTER illegal address (verified) update_unibus_address(unibus_address); // set addr msb to cs if (error_dma_timeout) { @@ -802,7 +809,7 @@ void RL11_c::state_readwrite() { // thread // excutes commands void RL11_c::worker(unsigned instance) { - UNUSED(instance) ; // only one + UNUSED(instance); // only one assert(!pthread_mutex_lock(&on_after_register_access_mutex)); // set prio to RT, but less than unibus_adapter diff --git a/10.02_devices/2_src/rl11.hpp b/10.02_devices/2_src/rl11.hpp index 7498dcd..8dfaf4b 100644 --- a/10.02_devices/2_src/rl11.hpp +++ b/10.02_devices/2_src/rl11.hpp @@ -26,12 +26,13 @@ #ifndef _RL11_HPP_ #define _RL11_HPP_ +#include "unibusadapter.hpp" #include "storagecontroller.hpp" class RL0102_c; class RL11_c: public storagecontroller_c { private: - /** everything sharead between different threads must be "volatile" */ + /** everything shared between different threads must be "volatile" */ volatile unsigned state; // one of RL11_STATE_* timeout_c timeout; @@ -58,6 +59,10 @@ private: uint16_t silo[128]; // buffer from/to drive uint16_t silo_compare[128]; // memory data to be compared with silo + // RL11 has one INTR and DMA + dma_request_c dma_request = dma_request_c(this); // operated by unibusadapter + intr_request_c intr_request = intr_request_c(this); + // only 16*16 = 256 byte buffer from drive (SILO) // one DMA transaction per sector, must be complete within one sector time // (2400 rpm = 40 rounds per second -> 1/160 sec per sector = 6,25 millisecs diff --git a/10.02_devices/2_src/rs232.cpp b/10.02_devices/2_src/rs232.cpp index c412c8e..0b664ee 100644 --- a/10.02_devices/2_src/rs232.cpp +++ b/10.02_devices/2_src/rs232.cpp @@ -227,7 +227,7 @@ int rs232_c::OpenComport(const char *devname, int baudrate, const char *mode, */ int iflag; if (par_and_break) - iflag = PARMRK | INPCK; + iflag = PARMRK | INPCK; else iflag = ipar; diff --git a/10.02_devices/2_src/testcontroller.cpp b/10.02_devices/2_src/testcontroller.cpp new file mode 100644 index 0000000..87bf27d --- /dev/null +++ b/10.02_devices/2_src/testcontroller.cpp @@ -0,0 +1,238 @@ +/* testcontroller.cpp: sample UNIBUS controller with selftest logic + + Copyright (c) 2018-2019, Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + 23-jul-2019 JH added interrupt and DMA functions + 12-nov-2018 JH entered beta phase + + "Tester" is a device to test the event, stress INTR and DMA, and + implements 32 registers at start of IOpage + + Controller registers: + ------------------------ + 32 registers @ 0760200.. 0760276 + all registers are marked as "active": + DATI and DATO are routed via events into the controller logic + UNIBUS is stopped with long SSYN + + +0 = CSR: write command, read = status + + other registers have no functions, are simple memory cells. + + Backplane Slots: 10,11,12 + + DMA + --- + has 2 DMA channels on different priority_slots + Used for priroity test of parallel DMA req with different slot priority + + INTR + --- + 4 x 3 interrupts (BR4,5,6,7 at slot 10,11,12) + To test slot priority and level priroity + raise all simulataneously with CPU level = 7 + -> no INTR triggered + lower CPU lvel to 6 -> 2 INTR in increasing slot priority triggered + + + + + Test #1 DMA priority test + ------------------------ + Write of 1 into CSR triggers test + First a long 1K DMA "A" with lower slot priority is started (DEPOSIT) + 2nd a DMA "B" with higher slot priority is started + After some A-chunks, B get priorized and is completed earlier, despit started later. + Verify: At mem start, "B" values re found (B later), + at mem end "A" values are found (runs later) + + + + + */ +#include +#include +#include +//#include + +#include "utils.hpp" +#include "logger.hpp" +#include "unibus.h" +#include "unibusadapter.hpp" +#include "unibusdevice.hpp" // definition of class device_c +#include "testcontroller.hpp" + +testcontroller_c::testcontroller_c() : + unibusdevice_c() // super class constructor +{ + unsigned i; + + // static config + name.value = "Test controller"; + type_name.value = "testcontroller_c"; + log_label = "tc"; + + // mem at 160000: RT11 crashes? + set_default_bus_params(0760200, 16, 0, 0); // base addr, priority_slot, intr-vector, intr level + // + + register_count = 32; // up to 760200 .. 760276 + + // CSR + CSR = &(this->registers[0]); + strcpy(CSR->name, "CSR"); + CSR->active_on_dati = true; // controller state change on read + CSR->active_on_dato = true; // writing changes controller state + CSR->reset_value = 0; + CSR->writable_bits = 0xffff; // all registers are memory cells + + // Other registers are "active": receive "on_after_register_access" + for (i = 1; i < this->register_count; i++) { + unibusdevice_register_t *reg = &(this->registers[i]); + sprintf(reg->name, "reg%02o", i); // name is register offset: "reg07" + reg->active_on_dati = true; // controller state change on read + reg->active_on_dato = true; // writing changes controller state + reg->reset_value = 0; + reg->writable_bits = 0xffff; // all registers are memory cells + } + + // create DMA requests. + for (unsigned i = 0; i < dma_channel_count; i++) { + dma_request_c *dmareq = dma_channel_request[i] = new dma_request_c(this); + // lowest index = highest slot priority + dmareq->set_priority_slot(i + 15); + dma_channel_buffer[i] = new memoryimage_c(); + + } + // create INTR requests. + for (unsigned slot = 1; slot < PRIORITY_SLOT_COUNT; slot++) + for (unsigned level_index = 0; level_index < 4; level_index++) { + intr_request_c *intreq = intr_request[slot][level_index] = new intr_request_c(this); + intreq->set_priority_slot(slot); + intreq->set_level(level_index+4); + // "vector" uninitialized, must be set on use! + } + + // dynamic state + access_count.value = 0; + +} + +testcontroller_c::~testcontroller_c() { + for (unsigned i = 0; i < dma_channel_count; i++) { + delete dma_channel_request[i]; + delete dma_channel_buffer[i]; + } + + for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++) + for (unsigned level_index = 0; level_index < 4; level_index++) + delete intr_request[slot][level_index]; +} + +bool testcontroller_c::on_param_changed(parameter_c *param) { + // no own parameter or "enable" logic + return unibusdevice_c::on_param_changed(param); // more actions (for enable) +} + +// process DATI/DATO access to one of my "active" registers +// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS. +// The time between PRU event and program flow into this callback +// is determined by ARM Linux context switch +// +// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc: +// do not read back dati_flipflops. +void testcontroller_c::on_after_register_access(unibusdevice_register_t *device_reg, + uint8_t unibus_control) { + + // emulate a plain memory cell: written values can be read unchanged + if (unibus_control == UNIBUS_CONTROL_DATI) { + } + + if (unibus_control == UNIBUS_CONTROL_DATO) + set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); + if (device_reg == CSR) { + /// CSR has been written: signal worker() + pthread_cond_signal(&on_after_register_access_cond); + // worker now executes CSR command and clears datao lfipflops + } + // this is also called for some DATIs, no action anyhow. + + access_count.value++; + // DEBUG writes to disk & console ... measured delay up to 30ms ! + //DEBUG(LL_DEBUG, LC_demo_regs, "[%6u] reg +%d @ %06o %s", accesscount, (int ) device_reg->index, + // device_reg->addr, unibus_c::control2text(unibus_control)); +} + +void testcontroller_c::on_power_changed(void) { + if (power_down) { // power-on defaults + } +} + +// UNIBUS INIT: clear all registers +void testcontroller_c::on_init_changed(void) { + // write all registers to "reset-values" + if (init_asserted) { + reset_unibus_registers(); + INFO("testcontroller_c::on_init()"); + } +} + +// background worker. +// Just print a heart beat +void testcontroller_c::worker(unsigned instance) { + UNUSED(instance); // only one + timeout_c timeout; + assert(!pthread_mutex_lock(&on_after_register_access_mutex)); + + // set prio to RT, but less than unibus_adapter + worker_init_realtime_priority(rt_device); + + while (!workers_terminate) { + + int res = pthread_cond_wait(&on_after_register_access_cond, + &on_after_register_access_mutex); + if (res != 0) { + ERROR("testcontroller_c::worker() pthread_cond_wait = %d = %s>", res, + strerror(res)); + continue; + } + // execute command in CSR, + uint16_t cmd = CSR->active_dato_flipflops; + // mark as processed + CSR->active_dato_flipflops = 0; + set_register_dati_value(CSR, 0, __func__); // no status + switch (cmd) { + case 1: + test_dma_priority(); + break; + + } + + timeout.wait_ms(1000); + cout << "."; + } + + assert(!pthread_mutex_unlock(&on_after_register_access_mutex)); +} + +void testcontroller_c::test_dma_priority() { +} + diff --git a/10.02_devices/2_src/demo_regs.hpp b/10.02_devices/2_src/testcontroller.hpp similarity index 66% rename from 10.02_devices/2_src/demo_regs.hpp rename to 10.02_devices/2_src/testcontroller.hpp index 5a8b8e1..ff3d434 100644 --- a/10.02_devices/2_src/demo_regs.hpp +++ b/10.02_devices/2_src/testcontroller.hpp @@ -1,6 +1,6 @@ -/* demo_regs.hpp: sample UNIBUS controller with register logic +/* testcontroller.hpp: sample UNIBUS controller with selftest logic - Copyright (c) 2018, Joerg Hoppe + Copyright (c) 2018-2019, Joerg Hoppe j_hoppe@t-online.de, www.retrocmp.com Permission is hereby granted, free of charge, to any person obtaining a @@ -21,24 +21,39 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + 23-jul-2019 JH added interrupt and DMA functions 12-nov-2018 JH entered beta phase */ -#ifndef _DEMO_REGS_HPP_ -#define _DEMO_REGS_HPP_ +#ifndef _TESTCONTROLLER_HPP_ +#define _TESTCONTROLLER_HPP_ #include "utils.hpp" #include "unibusdevice.hpp" +#include "memoryimage.hpp" #include "parameter.hpp" -class demo_regs_c: public unibusdevice_c { +class testcontroller_c: public unibusdevice_c { private: public: + unibusdevice_register_t *CSR; // command and status register + parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count", "ac",/*readonly*/ true, "", "%u", "Total # of register accesses", 32, 10); - demo_regs_c(); + // For arbitray tests of the priority request system, we have + // one reqiest for every slot/level combination + static const unsigned dma_channel_count = 2; + dma_request_c *dma_channel_request[dma_channel_count]; + // for concurrent DMA, testcontroller needs one data buffer per possible DMA. + // these are n*4MB ! + memoryimage_c *dma_channel_buffer[dma_channel_count]; + + intr_request_c *intr_request[PRIORITY_SLOT_COUNT][4]; // 3 slots, 4 levels + + testcontroller_c(); + ~testcontroller_c(); bool on_param_changed(parameter_c *param) override; // must implement @@ -51,6 +66,8 @@ public: void on_power_changed(void) override; void on_init_changed(void) override; + + void test_dma_priority(void); }; #endif diff --git a/10.02_devices/2_src/uda.cpp b/10.02_devices/2_src/uda.cpp index ccda235..2fbef44 100644 --- a/10.02_devices/2_src/uda.cpp +++ b/10.02_devices/2_src/uda.cpp @@ -46,7 +46,12 @@ uda_c::uda_c() : type_name.value = "UDA50"; log_label = "uda"; - set_default_bus_params(0772150, 0154, 5) ; // base addr, intr-vector, intr level + // base addr, intr-vector, intr level + set_default_bus_params(0772150, 20, 0154, 5) ; + dma_request.set_priority_slot(default_priority_slot) ; + intr_request.set_priority_slot(default_priority_slot) ; + intr_request.set_level(default_intr_level) ; + intr_request.set_vector(default_intr_vector) ; // The UDA50 controller has two registers. register_count = 2; @@ -807,7 +812,7 @@ uda_c::Interrupt(void) { if ((_interruptEnable || _initStep == InitializationStep::Complete) && _interruptVector != 0) { - interrupt(); + unibusadapter->INTR(intr_request, NULL, 0); // todo: link to interupt register } } @@ -940,11 +945,12 @@ uda_c::DMAWrite( assert ((lengthInBytes % 2) == 0); assert (address < 0x40000); - return unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, address, reinterpret_cast(buffer), - lengthInBytes >> 1, NULL); + lengthInBytes >> 1); + return dma_request.success ; } // @@ -971,13 +977,13 @@ uda_c::DMARead( memset(reinterpret_cast(buffer), 0xc3, bufferSize); - bool success = unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, address, buffer, - lengthInBytes >> 1, NULL); + lengthInBytes >> 1); - if (success) + if (dma_request.success) { return reinterpret_cast(buffer); } diff --git a/10.02_devices/2_src/uda.hpp b/10.02_devices/2_src/uda.hpp index afa4bdc..b7e861c 100644 --- a/10.02_devices/2_src/uda.hpp +++ b/10.02_devices/2_src/uda.hpp @@ -9,6 +9,7 @@ #include #include "utils.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" #include "storagecontroller.hpp" #include "mscp_server.hpp" @@ -73,6 +74,12 @@ public: void on_init_changed(void) override; void on_drive_status_changed(storagedrive_c *drive) override; + + + // As every storage controller UDA has one INTR and DMA + dma_request_c dma_request = dma_request_c(this) ; // operated by unibusadapter + intr_request_c intr_request = intr_request_c(this) ; + public: // diff --git a/10.02_devices/3_test/dl11w/ZDLDI0.BIN b/10.02_devices/3_test/dl11w/ZDLDI0.BIN new file mode 100644 index 0000000..baa3577 Binary files /dev/null and b/10.02_devices/3_test/dl11w/ZDLDI0.BIN differ diff --git a/10.03_app_demo/2_src/application.cpp b/10.03_app_demo/2_src/application.cpp index e2100c6..b0b56f0 100644 --- a/10.03_app_demo/2_src/application.cpp +++ b/10.03_app_demo/2_src/application.cpp @@ -59,6 +59,7 @@ using namespace std; #include "getopt2.hpp" #include "inputline.h" +#include "kbhit.h" #include "pru.hpp" #include "mailbox.h" #include "gpios.hpp" @@ -222,9 +223,8 @@ void application_c::parse_commandline(int argc, char **argv) { } // configure all hardware related subsystems: -// PUR, shard memory, GPIOs, +// PRU, shard memory, GPIOs void application_c::hardware_startup(enum pru_c::prucode_enum prucode_id) { - INFO("Connecting to PRU."); /* initialize the library, PRU and interrupt; launch our PRU program */ @@ -247,6 +247,13 @@ void application_c::hardware_startup(enum pru_c::prucode_enum prucode_id) { iopageregisters_init(); } +// if prucode_id is UNIBUS, define also wether Abritration ignored, doen by remote CPU or emulated +void application_c::hardware_startup(enum pru_c::prucode_enum prucode_id, + enum unibus_c::arbitration_mode_enum arbitration_mode) { + hardware_startup(prucode_id); + unibus_c::set_arbitration_mode(arbitration_mode); +} + // disable all hardware related subsystems: void application_c::hardware_shutdown() { pru->stop(); @@ -320,7 +327,7 @@ static void factory() { pru = new pru_c(); gpios = new gpios_c(); - unibus_signals = new unibus_signals_c() ; + unibus_signals = new unibus_signals_c(); ddrmem = new ddrmem_c(); // paneldriver before all devices who use lamps or buttons @@ -329,12 +336,18 @@ static void factory() { membuffer = new memoryimage_c(); unibus = new unibus_c(); + // unibusadapter.worker() needs initialized mailbox unibusadapter = new unibusadapter_c(); app = new application_c(); } int main(int argc, char *argv[]) { + + // flush stuff on stdin. (Eclipse remote debugging) + while (os_kbhit()) + ; + factory(); return app->run(argc, argv); } diff --git a/10.03_app_demo/2_src/application.hpp b/10.03_app_demo/2_src/application.hpp index 4d5620b..30fb385 100644 --- a/10.03_app_demo/2_src/application.hpp +++ b/10.03_app_demo/2_src/application.hpp @@ -43,7 +43,7 @@ #include "unibusdevice.hpp" #define PROGNAME "demo" -#define VERSION "v1.1.0" +#define VERSION "v1.5.0" class application_c: public logsource_c { public: @@ -68,19 +68,27 @@ public: void parse_commandline(int argc, char **argv); void hardware_startup(enum pru_c::prucode_enum prucode_id); + void hardware_startup(enum pru_c::prucode_enum prucode_id, + enum unibus_c::arbitration_mode_enum arbitration_mode); + void hardware_shutdown(void); // UniBone should emulate this address range uint32_t emulated_memory_start_addr; uint32_t emulated_memory_end_addr; - void print_arbitration_info(enum unibus_c::arbitration_mode_enum arbitration_mode, const char *indent); - char *getchoice(void); - bool emulate_memory(enum unibus_c::arbitration_mode_enum arbitration_mode); + void print_arbitration_info(enum unibus_c::arbitration_mode_enum arbitration_mode, + const char *indent); + char *getchoice(void);bool emulate_memory( + enum unibus_c::arbitration_mode_enum arbitration_mode); void print_params(parameterized_c *parameterized, parameter_c *p); unibusdevice_register_t * device_register_by_id(unibusdevice_c *device, char *specifier); + bool parse_word(char *txt, uint16_t *word);bool parse_addr18(char *txt, uint32_t *addr);bool parse_level( + char *txt, uint8_t *level);bool parse_vector(char *txt, uint16_t max_vector, + uint16_t *vector);bool parse_slot(char *txt, uint8_t *priority_slot); + void menu_info(void); void menu_gpio(void); void menu_panel(void); diff --git a/10.03_app_demo/2_src/makefile b/10.03_app_demo/2_src/makefile index f40f4ef..bded5b9 100644 --- a/10.03_app_demo/2_src/makefile +++ b/10.03_app_demo/2_src/makefile @@ -23,8 +23,8 @@ PRUSS_INCLUDE_DIR = $(PRU_PACKAGE_ROOT)/include # ARM LIBRARIES PRUSS_DRV_LIB = $(PRU_PACKAGE_ROOT)/bin-bbb/libprussdrv.a -# -static: do not used shared libs, include all coide into the binary -# (big binary, but BBB neds no shared libs of certain versions installed) +# -static: do not used shared libs, include all code into the binary +# (big binary, but BBB needs no shared libs of certain versions installed) # Example: demo binary goes from 594K to 12.3MB ! LDFLAGS+= -static -lstdc++ -lpthread $(PRUSS_DRV_LIB) @@ -109,17 +109,19 @@ OBJECTS = $(OBJDIR)/application.o \ $(OBJDIR)/storagedrive.o \ $(OBJDIR)/storagecontroller.o \ $(OBJDIR)/demo_io.o \ - $(OBJDIR)/demo_regs.o \ + $(OBJDIR)/testcontroller.o \ $(OBJDIR)/unibusdevice.o \ $(OBJDIR)/device.o \ $(OBJDIR)/parameter.o \ $(OBJDIR)/panel.o \ + $(OBJDIR)/priorityrequest.o \ $(OBJDIR)/unibusadapter.o \ $(OBJDIR)/unibus.o \ $(OBJDIR)/gpios.o \ $(OBJDIR)/stringgrid.o \ $(OBJDIR)/mcout.o \ $(OBJDIR)/inputline.o \ + $(OBJDIR)/kbhit.o \ $(OBJDIR)/bitcalc.o \ $(OBJDIR)/pru.o \ $(OBJDIR)/mailbox.o \ @@ -250,7 +252,7 @@ $(OBJDIR)/storagecontroller.o : $(BASE_SRC_DIR)/storagecontroller.cpp $(BASE_SR $(OBJDIR)/demo_io.o : $(DEVICE_SRC_DIR)/demo_io.cpp $(DEVICE_SRC_DIR)/demo_io.hpp $(CC) $(CCFLAGS) $< -o $@ -$(OBJDIR)/demo_regs.o : $(DEVICE_SRC_DIR)/demo_regs.cpp $(DEVICE_SRC_DIR)/demo_regs.hpp +$(OBJDIR)/testcontroller.o : $(DEVICE_SRC_DIR)/testcontroller.cpp $(DEVICE_SRC_DIR)/testcontroller.hpp $(CC) $(CCFLAGS) $< -o $@ $(OBJDIR)/unibusdevice.o : $(BASE_SRC_DIR)/unibusdevice.cpp $(BASE_SRC_DIR)/unibusdevice.hpp @@ -280,6 +282,9 @@ $(OBJDIR)/mcout.o : $(COMMON_SRC_DIR)/mcout.c $(COMMON_SRC_DIR)/mcout.h $(OBJDIR)/inputline.o : $(COMMON_SRC_DIR)/inputline.c $(COMMON_SRC_DIR)/inputline.h $(CC) $(CCFLAGS) -xc++ $< -o $@ +$(OBJDIR)/kbhit.o : $(COMMON_SRC_DIR)/kbhit.c $(COMMON_SRC_DIR)/kbhit.h + $(CC) $(CCFLAGS) -xc++ $< -o $@ + $(OBJDIR)/bitcalc.o : $(COMMON_SRC_DIR)/bitcalc.cpp $(COMMON_SRC_DIR)/bitcalc.h $(CC) $(CCFLAGS) $< -o $@ @@ -317,6 +322,9 @@ $(OBJDIR)/ddrmem.o : $(BASE_SRC_DIR)/ddrmem.cpp $(SHARED_SRC_DIR)/ddrmem.h $(OBJDIR)/iopageregister.o : $(BASE_SRC_DIR)/iopageregister.cpp $(SHARED_SRC_DIR)/iopageregister.h $(CC) $(CCFLAGS) $< -o $@ +$(OBJDIR)/priorityrequest.o : $(BASE_SRC_DIR)/priorityrequest.cpp $(BASE_SRC_DIR)/priorityrequest.hpp + $(CC) $(CCFLAGS) $< -o $@ + $(OBJDIR)/unibusadapter.o : $(BASE_SRC_DIR)/unibusadapter.cpp $(BASE_SRC_DIR)/unibusadapter.hpp $(CC) $(CCFLAGS) $< -o $@ diff --git a/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp b/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp index 6d5de50..8514aa5 100644 --- a/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp +++ b/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp @@ -47,7 +47,7 @@ void application_c::menu_ddrmem_slave_only() { bool ready; int n_fields; - hardware_startup(pru_c::PRUCODE_UNIBUS) ; + hardware_startup(pru_c::PRUCODE_UNIBUS, unibus_c::ARBITRATION_MODE_NONE); ready = false; while (!ready) { @@ -93,8 +93,9 @@ void application_c::menu_ddrmem_slave_only() { else printf("Use \"f a\" or \"f p\"\n"); } else if (!strcasecmp(s_opcode, "u") && (n_fields == 3)) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); if (ddrmem->set_range(start_addr, end_addr)) { emulated_memory_start_addr = start_addr; emulated_memory_end_addr = end_addr; @@ -117,7 +118,7 @@ void application_c::menu_ddrmem_slave_only() { show_help = true; } } - hardware_shutdown() ; - + hardware_shutdown(); + } diff --git a/10.03_app_demo/2_src/menu_device_exercisers.cpp b/10.03_app_demo/2_src/menu_device_exercisers.cpp index 577f734..1eacc65 100644 --- a/10.03_app_demo/2_src/menu_device_exercisers.cpp +++ b/10.03_app_demo/2_src/menu_device_exercisers.cpp @@ -40,6 +40,7 @@ #include "parameter.hpp" #include "unibus.h" #include "memoryimage.hpp" +#include "unibusadapter.hpp" //#include "unibusadapter.hpp" //#include "unibusdevice.hpp" @@ -58,7 +59,7 @@ void application_c::menu_device_exercisers(void) { // iopageregisters_init(); // UNIBUS activity - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, unibus_c::ARBITRATION_MODE_CLIENT); buslatches_output_enable(true); // instantiate different device exercisers @@ -82,8 +83,7 @@ void application_c::menu_device_exercisers(void) { } else printf(" No current device selected\n"); if (memory_installed) { - printf( - " UNIBUS memory emulated from %06o to %06o.\n", + printf(" UNIBUS memory emulated from %06o to %06o.\n", emulated_memory_start_addr, emulated_memory_end_addr); } else printf(" NO UNIBUS memory installed ... device test limited!\n"); @@ -143,17 +143,14 @@ void application_c::menu_device_exercisers(void) { bool timeout; uint16_t fillword = 0; if (n_fields == 3) - fillword = strtol(s_param[1], NULL, 8); + parse_word(s_param[1], &fillword); membuffer->set_addr_range(emulated_memory_start_addr, emulated_memory_end_addr); membuffer->fill(fillword); // write buffer-> UNIBUS - printf( - "Fill memory with %06o, writing UNIBUS memory[%06o:%06o] with blocksize %u words\n", - fillword, emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount); + printf("Fill memory with %06o, writing UNIBUS memory[%06o:%06o]\n", fillword, + emulated_memory_start_addr, emulated_memory_end_addr); unibus->mem_write(arbitration_mode, membuffer->data.words, - emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount, &timeout); + emulated_memory_start_addr, emulated_memory_end_addr, &timeout); if (timeout) printf("Error writing UNIBUS memory!\n"); } else if (memory_installed && !strcasecmp(s_opcode, "m") && n_fields == 2 @@ -163,14 +160,13 @@ void application_c::menu_device_exercisers(void) { bool timeout; // 1. read UNIBUS memory uint32_t end_addr = unibus->test_sizer(arbitration_mode) - 2; - printf("Reading UNIBUS memory[0:%06o] with DMA blocks of %u words\n", end_addr, - unibus->dma_wordcount); + printf("Reading UNIBUS memory[0:%06o] with DMA\n", end_addr); // clear memory buffer, to be sure content changed membuffer->set_addr_range(0, end_addr); membuffer->fill(0); unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, - unibus->dma_wordcount, &timeout); + &timeout); if (timeout) printf("Error reading UNIBUS memory!\n"); else { @@ -226,27 +222,27 @@ void application_c::menu_device_exercisers(void) { } } else if (!strcasecmp(s_opcode, "d") && n_fields == 3) { uint32_t addr; + uint16_t wordbuffer; // interpret as 18 bit address - addr = strtol(s_param[0], NULL, 8); - - mailbox->dma.words[0] = strtol(s_param[1], NULL, 8); - bool timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATO, addr, 1); - printf("DEPOSIT %06o <- %06o\n", addr, mailbox->dma.words[0]); + parse_addr18(s_param[0], &addr); + parse_word(s_param[1], &wordbuffer); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, addr, + &wordbuffer, 1); + printf("DEPOSIT %06o <- %06o\n", addr, wordbuffer); if (timeout) - printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); + printf("Bus timeout at %06o.\n", unibus->dma_request->unibus_end_addr); } else if (!strcasecmp(s_opcode, "e") && n_fields <= 2) { - unsigned blocksize = 0; // default: no EXAM - bool timeout; + uint16_t wordbuffer; + bool timeout=false; uint32_t addr; if (n_fields == 2) { // single reg number given - blocksize = 1; // exam 1 word - addr = strtol(s_param[0], NULL, 8); // interpret as 18 bit address - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, - blocksize); - printf("EXAM %06o -> %06o\n", addr, mailbox->dma.words[0]); + parse_addr18(s_param[0], &addr); // interpret as 18 bit address + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, addr, + &wordbuffer, 1); + printf("EXAM %06o -> %06o\n", addr, wordbuffer); } if (timeout) - printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); + printf("Bus timeout at %06o.\n", addr); // cur_addr now on last address in block } else { diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index 801b4d6..84ab102 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -48,7 +48,7 @@ #include "storagedrive.hpp" #include "panel.hpp" #include "demo_io.hpp" -#include "demo_regs.hpp" +#include "testcontroller.hpp" #include "rl11.hpp" #include "rk11.hpp" #include "uda.hpp" @@ -81,19 +81,31 @@ static void load_memory(enum unibus_c::arbitration_mode_enum arbitration_mode, printf( "Loaded MACRO-11 listing from file \"%s\" into memory: %d words from %06o to %06o.\n", fname, membuffer->get_word_count(), firstaddr, lastaddr); - if (membuffer->entry_address != MEMORY_ADDRESS_INVALID) + if (entry_label == NULL) + printf(" No entry address label.\n") ; + else if (membuffer->entry_address != MEMORY_ADDRESS_INVALID) printf(" Entry address at \"%s\" label is %06o.\n", entry_label, membuffer->entry_address); else printf(" No entry address at \"%s\" label is %06o.\n", entry_label, membuffer->entry_address); unibus->mem_write(arbitration_mode, membuffer->data.words, firstaddr, lastaddr, - unibus->dma_wordcount, &timeout); + &timeout); if (timeout) printf(" Error writing UNIBUS memory\n"); } } +static void print_device(device_c *device) { + unibusdevice_c *ubdevice = dynamic_cast(device); + if (ubdevice) + printf("- %-12s Type %s, %s.\n",ubdevice->name.value.c_str(), + ubdevice->type_name.value.c_str(), ubdevice->get_unibus_resource_info()) ; + else + printf("- %-12s Type %s.\n",device->name.value.c_str(), + device->type_name.value.c_str()) ; +} + // CPU is enabled, act as ARBITRATION_MASTER void application_c::menu_devices(bool with_CPU) { /** list of usable devices ***/ @@ -121,7 +133,7 @@ void application_c::menu_devices(bool with_CPU) { // iopageregisters_init(); // UNIBUS activity // assert(unibus->arbitrator_client) ; // External Bus Arbitrator required - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, arbitration_mode); // now PRU executing UNIBUS master/slave code, physical PDP-11 CPU as arbitrator required. buslatches_output_enable(true); @@ -130,7 +142,10 @@ void application_c::menu_devices(bool with_CPU) { // 2 demo controller cur_device = NULL; demo_io_c *demo_io = new demo_io_c(); - //demo_regs_c demo_regs; // mem at 160000: RT11 crashes? + + // uses all slot resource, can onyl run alone + //testcontroller_c *test_controller = new testcontroller_c(); + cpu_c *cpu = NULL; // create RL11 + also 4 RL01/02 drives RL11_c *RL11 = new RL11_c(); @@ -190,6 +205,8 @@ void application_c::menu_devices(bool with_CPU) { printf("m ll Reload last memory content from file \"%s\"\n", memory_filename); printf("m lp Load memory content from absolute papertape image\n"); + printf("m lp Reload last memory content from file \"%s\"\n", + memory_filename); printf("ld List all defined devices\n"); printf("en Enable a device\n"); printf("dis Disable device\n"); @@ -244,17 +261,14 @@ void application_c::menu_devices(bool with_CPU) { bool timeout; uint16_t fillword = 0; if (n_fields == 3) - fillword = strtol(s_param[1], NULL, 8); + parse_word(s_param[1], &fillword); membuffer->set_addr_range(emulated_memory_start_addr, emulated_memory_end_addr); membuffer->fill(fillword); // write buffer-> UNIBUS - printf( - "Fill memory with %06o, writing UNIBUS memory[%06o:%06o] with blocksize %u words\n", - fillword, emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount); + printf("Fill memory with %06o, writing UNIBUS memory[%06o:%06o]\n", fillword, + emulated_memory_start_addr, emulated_memory_end_addr); unibus->mem_write(arbitration_mode, membuffer->data.words, - emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount, &timeout); + emulated_memory_start_addr, emulated_memory_end_addr, &timeout); if (timeout) printf("Error writing UNIBUS memory!\n"); } else if (!strcasecmp(s_opcode, "m") && n_fields == 2 @@ -264,14 +278,13 @@ void application_c::menu_devices(bool with_CPU) { bool timeout; // 1. read UNIBUS memory uint32_t end_addr = unibus->test_sizer(arbitration_mode) - 2; - printf("Reading UNIBUS memory[0:%06o] with DMA blocks of %u words\n", end_addr, - unibus->dma_wordcount); + printf("Reading UNIBUS memory[0:%06o] with DMA\n", end_addr); // clear memory buffer, to be sure content changed membuffer->set_addr_range(0, end_addr); membuffer->fill(0); unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, - unibus->dma_wordcount, &timeout); + &timeout); if (timeout) printf("Error reading UNIBUS memory!\n"); else { @@ -292,6 +305,10 @@ void application_c::menu_devices(bool with_CPU) { && !strcasecmp(s_param[0], "lp")) { // m lp load_memory(arbitration_mode, fileformat_papertape, s_param[1], NULL); + } else if (!strcasecmp(s_opcode, "m") && n_fields == 2 + && !strcasecmp(s_param[0], "lp") && strlen(memory_filename)) { + // m lp + load_memory(arbitration_mode, fileformat_papertape, memory_filename, NULL); } else if (!strcasecmp(s_opcode, "ld") && n_fields == 1) { unsigned n; list::iterator it; @@ -301,10 +318,7 @@ void application_c::menu_devices(bool with_CPU) { if (n == 0) cout << "Enabled devices:\n"; n++; - cout << "- " << (*it)->name.value << " (type is " - << (*it)->type_name.value << ")\n"; -// if ((*it)->name.value.empty()) -// printf("EMPTY\n"); + print_device(*it); } if (n == 0) cout << "No enabled devices.\n"; @@ -315,8 +329,7 @@ void application_c::menu_devices(bool with_CPU) { if (n == 0) cout << "Disabled devices:\n"; n++; - cout << "- " << (*it)->name.value << " (type is " - << (*it)->type_name.value << ")\n"; + print_device(*it); } if (n == 0) cout << "No disabled devices.\n"; @@ -383,53 +396,57 @@ void application_c::menu_devices(bool with_CPU) { } } else if (unibuscontroller && !strcasecmp(s_opcode, "d") && n_fields == 3) { uint32_t addr; + uint16_t wordbuffer; unibusdevice_register_t *reg = unibuscontroller->register_by_name(s_param[0]); if (reg) // register name given addr = reg->addr; else // interpret as 18 bit address - addr = strtol(s_param[0], NULL, 8); + parse_addr18(s_param[0], &addr); - mailbox->dma.words[0] = strtol(s_param[1], NULL, 8); - bool timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATO, addr, 1); + parse_word(s_param[1], &wordbuffer); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, addr, + &wordbuffer, 1); if (reg) { assert( reg == unibuscontroller->register_by_unibus_address( - mailbox->dma.cur_addr)); + unibus->dma_request->unibus_end_addr)); printf("DEPOSIT reg #%d \"%s\" %06o <- %06o\n", reg->index, reg->name, - reg->addr, mailbox->dma.words[0]); + reg->addr, wordbuffer); } else - printf("DEPOSIT %06o <- %06o\n", addr, mailbox->dma.words[0]); + printf("DEPOSIT %06o <- %06o\n", addr, wordbuffer); if (timeout) printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); } else if (unibuscontroller && !strcasecmp(s_opcode, "e") && n_fields <= 2) { - unsigned blocksize = 0; // default: no EXAM bool timeout; uint32_t addr; + unibusdevice_register_t *reg; if (n_fields == 2) { // single reg number given - blocksize = 1; // exam 1 word + uint16_t wordbuffer; // exam single word reg = unibuscontroller->register_by_name(s_param[0]); if (reg) addr = reg->addr; else - addr = strtol(s_param[0], NULL, 8); // interpret as 18 bit address - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, - blocksize); - printf("EXAM %06o -> %06o\n", addr, mailbox->dma.words[0]); + parse_addr18(s_param[0], &addr); // interpret as 18 bit address + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, addr, + &wordbuffer, 1); + printf("EXAM %06o -> %06o\n", addr, wordbuffer); } else { // list all regs + unsigned wordcount = 0; // default: no EXAM + uint16_t wordbuffer[MAX_REGISTERS_PER_DEVICE]; addr = unibuscontroller->base_addr.value; // all device registers - blocksize = unibuscontroller->register_count; - if (blocksize) { + wordcount = unibuscontroller->register_count; + if (wordcount) { unsigned i; - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, - blocksize); + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, + addr, wordbuffer, wordcount); for (i = 0; addr <= mailbox->dma.cur_addr; i++, addr += 2) { reg = unibuscontroller->register_by_unibus_address(addr); assert(reg); printf("EXAM reg #%d %s %06o -> %06o\n", reg->index, reg->name, - reg->addr, mailbox->dma.words[i]); + reg->addr, wordbuffer[i]); } } else { timeout = false; @@ -468,8 +485,8 @@ void application_c::menu_devices(bool with_CPU) { UDA50->enabled.set(false); delete UDA50; -// //demo_regs.worker_stop(); -// //demo_regs.uninstall(); + //test_controller->enabled.set(false); + //delete test_controller; demo_io->enabled.set(false); delete demo_io; diff --git a/10.03_app_demo/2_src/menu_interrupts.cpp b/10.03_app_demo/2_src/menu_interrupts.cpp index f3a352a..330ecdb 100644 --- a/10.03_app_demo/2_src/menu_interrupts.cpp +++ b/10.03_app_demo/2_src/menu_interrupts.cpp @@ -39,25 +39,30 @@ #include "memoryimage.hpp" #include "unibusadapter.hpp" +#include "testcontroller.hpp" void application_c::menu_interrupts(void) { // needs physical CPU enum unibus_c::arbitration_mode_enum arbitration_mode = unibus_c::ARBITRATION_MODE_CLIENT; - const char *testprogram_fname = "intrtst.lst"; bool show_help = true; // show cmds on first screen, then only on error or request bool active = false; // 1 if PRU executes slave&master logic bool ready; + bool test_loaded; char *s_choice; char s_opcode[256], s_param[5][256]; int n_fields; + testcontroller_c *test_controller = new testcontroller_c(); + test_controller->enabled.value = true; + // These test need active PRUs // and an PDP-11 CPU as Arbitrator - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, arbitration_mode); buslatches_output_enable(true); ready = false; + test_loaded = false; while (!ready) { if (show_help) { show_help = false; // only once @@ -74,26 +79,38 @@ void application_c::menu_interrupts(void) { printf("***\n"); printf("*** Starting full UNIBUS master/slave logic on PRU\n"); printf("***\n"); - unibusadapter->enabled.set(true) ; + unibusadapter->enabled.set(true); active = true; } - - printf( - "a Automatic setup: emulate memory, download \"%s\" onto PDP-11.\n", - testprogram_fname); - printf( - "i Issue interrupt at priority to < [octal]\n"); - printf(" = 0..7, = 0,4,10,...,374\n"); - printf(" The test program \"%s\" must be running!\n", - testprogram_fname); - printf( - " Then interrupts cause print-out, and processor priority\n"); - printf(" can be set with keys 0..7.\n"); - printf(" Examples:\n"); - printf(" \"i 5 164\" calls vector 164 at level 5.\n"); - printf( - " If processor level < 5, INTR is accepted, a message is printed.\n"); - printf(" Else INTR is pending until level is lowered.\n"); + printf("m emulate all missing memory\n"); + printf("e EXAMINE the word at . [octal]\n"); + printf("d DEPOSIT into [octal]\n"); + printf("ll Load test program from MACRO-11 listing\n"); + if (test_loaded) { + printf( + "i Issue interrupt at priority to < [octal]\n"); + printf(" = 0..7, = 0,4,10,...,374\n"); + printf( + " Then interrupts cause print-out, and processor priority\n"); + printf(" can be set with keys 0..7.\n"); + printf(" Example:\n"); + printf(" \"i 5 164\" calls vector 164 at level 5.\n"); + printf( + " If processor level < 5, INTR is accepted, a message is printed.\n"); + printf(" Else INTR is pending until level is lowered.\n"); + printf(" = 0..7, = 0,4,10,...,374\n"); + printf( + "i Variant, additional a backplane slot for priority\n"); + printf(" within same level group is given\n"); + printf("dma (addr & data word octal)\n"); + printf( + " DEPOSIT memory range. Non-blocking, subsequent script commands\n"); + printf( + " are executed in parallel. is backplane slot for priority\n"); + printf(" 0..%u possible.\n", + (unsigned) test_controller->dma_channel_count); + } + printf("dl c|s|f Debug log: Clear, Show on console, dump to File.\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); printf("q Quit\n"); } @@ -106,61 +123,118 @@ void application_c::menu_interrupts(void) { ready = true; } else if (!strcasecmp(s_opcode, "pwr")) { unibus->powercycle(); - } else if (!strcasecmp(s_opcode, "a")) { - timeout_c timer; - bool load_ok, timeout; + } else if (!strcasecmp(s_opcode, "m") && n_fields == 1) { + emulate_memory(arbitration_mode); + } else if (!strcasecmp(s_opcode, "e") && n_fields == 2) { + uint32_t cur_addr; + uint16_t wordbuffer; + parse_addr18(s_param[0], &cur_addr); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, cur_addr, + &wordbuffer, 1); + if (timeout) + printf("Bus timeout at %06o.\n", cur_addr); + else + printf("EXAM %06o -> %06o\n", cur_addr, wordbuffer); + } else if (!strcasecmp(s_opcode, "d") && n_fields == 3) { + uint32_t cur_addr; + uint16_t wordbuffer; + parse_addr18(s_param[0], &cur_addr); + parse_word(s_param[1], &wordbuffer); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, cur_addr, + &wordbuffer, 1); + if (timeout) + printf("Bus timeout at %06o.\n", cur_addr); + else + printf("DEPOSIT %06o <- %06o\n", cur_addr, wordbuffer); + } else if (!strcasecmp(s_opcode, "ll") && n_fields == 2) { uint32_t start_addr, end_addr; - printf("Trying to start PDP-11 CPU...\n"); - unibus->powercycle(); - timer.wait_ms(1000); // shpw prompt - unibus->powercycle(); - timer.wait_ms(1000); // shpw prompt - - printf("Loading memory content from MACRO-11 listing \"%s\".\n", testprogram_fname); + bool timeout; + test_loaded = false; + printf("Loading memory content from MACRO-11 listing %s\n", s_param[0]); membuffer->init(); - load_ok = membuffer->load_macro11_listing(testprogram_fname, "start"); + bool load_ok = membuffer->load_macro11_listing(s_param[0], "start"); if (!load_ok) { printf("File load failed, aborting.\n"); - } else { - // SET_DEBUG_PIN0(1) ; - emulate_memory(arbitration_mode); - // SET_DEBUG_PIN0(0) ; - membuffer->get_addr_range(&start_addr, &end_addr); - printf( - "Loaded %u words, writing UNIBUS memory[%06o:%06o] with blocksize %u words.\n", - membuffer->get_word_count(), start_addr, end_addr, - unibus->dma_wordcount); - // SET_DEBUG_PIN1(1) ; - unibus->mem_write(arbitration_mode, membuffer->data.words, start_addr, end_addr, - unibus->dma_wordcount, &timeout); - // SET_DEBUG_PIN0(0) ; - // SET_DEBUG_PIN1(0) ; - if (timeout) - printf("Memory write failed with UNIBUS timeout, aborting.\n"); - else { - if (membuffer->entry_address == MEMORY_ADDRESS_INVALID) - printf( - "Start program manually on PDP-11 console (entry address not found).\n"); - else - printf( - "Start program manually on PDP-11 console, entry address is %06o.\n", - membuffer->entry_address); - } + continue; } - } else if (!strcasecmp(s_opcode, "i") && n_fields == 3) { - unsigned priority = strtol(s_param[0], NULL, 8); - unsigned vector = strtol(s_param[1], NULL, 8); - if (priority < 4 || priority > 7) { - printf("Illegal interrupt priority %u, must be 4..7.\n", priority); - } else if ((vector & 3) != 0) { - printf("Illegal interrupt vector %06o, must be multiple of 4.\n", vector); - } else if (vector >= 0374) { // artificial limit, still far to big - printf("Illegal interrupt vector %06o, must be <= 0374.\n", vector); - } else { - unibus->interrupt((uint8_t) priority, (uint16_t) vector); - printf("Interrupt with priority=%d, vector=%03o generated.\n", priority, - vector); + membuffer->get_addr_range(&start_addr, &end_addr); + printf("Loaded %u words, writing UNIBUS memory[%06o:%06o].\n", + membuffer->get_word_count(), start_addr, end_addr); + unibus->mem_write(arbitration_mode, membuffer->data.words, start_addr, end_addr, + &timeout); + if (timeout) + printf("Memory write failed with UNIBUS timeout, aborting.\n"); + else { + if (membuffer->entry_address == MEMORY_ADDRESS_INVALID) + printf( + "Start program manually on PDP-11 console (entry address not found).\n"); + else + printf("Start program manually on PDP-11 console, entry address is %06o.\n", + membuffer->entry_address); + test_loaded = true; } + } else if (test_loaded && !strcasecmp(s_opcode, "i") && n_fields >= 3 + && n_fields <= 4) { + + uint8_t priority_slot = 16; // default + uint8_t level; + uint16_t vector; + if (!parse_level(s_param[0], &level)) + continue; + if (n_fields == 3) { // i + if (!parse_vector(s_param[1], 0374, &vector)) // artificial limit, still far to big + continue; + } else if (n_fields == 4) { // i + if (!parse_slot(s_param[1], &priority_slot)) + continue; + if (!parse_vector(s_param[2], 0374, &vector)) // artificial limit, still far to big + continue; + } + // use request from testcontroller + unsigned level_idx = level - 4; // 4,5,6,7 -> 0,1,2,3 + intr_request_c *intr_request = + test_controller->intr_request[priority_slot][level_idx]; + intr_request->set_vector(vector); + unibusadapter->INTR(*intr_request, NULL, 0); + printf("Interrupt with level=%d, priority slot=%d, vector=%03o generated.\n", level, + priority_slot, vector); + } else if (test_loaded && !strcasecmp(s_opcode, "dma") && n_fields == 5) { + // dma + uint8_t dma_channel; + uint32_t addr_from, addr_to; + uint16_t fillword; + dma_channel = strtol(s_param[0], NULL, 10); + if (dma_channel >= test_controller->dma_channel_count) { + printf("Only DMA channels 0..%u possible.\n", + (unsigned) test_controller->dma_channel_count); + continue; + } + parse_addr18(s_param[1], &addr_from); + parse_addr18(s_param[2], &addr_to); + parse_word(s_param[3], &fillword); + if (addr_to < addr_from) + addr_to = addr_from; + unsigned wordcount = (addr_to - addr_from + 2) / 2; + + // do not use single global "membuffer": need independent buffer per concurrent DMA with different data + memoryimage_c *dma_buffer = test_controller->dma_channel_buffer[dma_channel]; + dma_buffer->set_addr_range(addr_from, addr_to); + dma_buffer->fill(fillword); + + // DMA running asynchronically parallel, no info on BUS timeout yet. User has to wait. + dma_request_c *dma_request = test_controller->dma_channel_request[dma_channel]; + unibusadapter->DMA(*dma_request, false, UNIBUS_CONTROL_DATO, addr_from, + &(dma_buffer->data.words[addr_from / 2]), wordcount); + printf("DEPOSIT in slot %d started for %06o..%06o\n", + dma_request->get_priority_slot(), addr_from, addr_to); + } else if (!strcasecmp(s_opcode, "dl") && n_fields == 2) { + if (!strcasecmp(s_param[0], "c")) { + logger->clear(); + printf("Debug log cleared.\n"); + } else if (!strcasecmp(s_param[0], "s")) + logger->dump(); + else if (!strcasecmp(s_param[0], "f")) + logger->dump(logger->default_filepath); } else { printf("Unknown command \"%s\"!\n", s_choice); show_help = true; @@ -170,9 +244,10 @@ void application_c::menu_interrupts(void) { printf("***\n"); printf("*** Stopping UNIBUS logic on PRU\n"); printf("***\n"); - unibusadapter->enabled.set(false) ; + unibusadapter->enabled.set(false); active = false; } + delete test_controller; // Switch off bus drivers buslatches_output_enable(false); diff --git a/10.03_app_demo/2_src/menu_mailbox.cpp b/10.03_app_demo/2_src/menu_mailbox.cpp index d76dddd..7878c45 100644 --- a/10.03_app_demo/2_src/menu_mailbox.cpp +++ b/10.03_app_demo/2_src/menu_mailbox.cpp @@ -45,7 +45,7 @@ void application_c::menu_mailbox() { char s_id[256], s_opcode[256]; ready = false; // test PRUs - hardware_startup(pru_c::PRUCODE_TEST) ; + hardware_startup(pru_c::PRUCODE_TEST); while (!ready) { if (show_help) { show_help = false; // only once @@ -70,6 +70,6 @@ void application_c::menu_mailbox() { show_help = true; } } - hardware_shutdown() ; + hardware_shutdown(); } diff --git a/10.03_app_demo/2_src/menu_masterslave.cpp b/10.03_app_demo/2_src/menu_masterslave.cpp index 1b2a841..ce1412c 100644 --- a/10.03_app_demo/2_src/menu_masterslave.cpp +++ b/10.03_app_demo/2_src/menu_masterslave.cpp @@ -44,14 +44,18 @@ #include "memoryimage.hpp" #include "unibusadapter.hpp" -#include "demo_regs.hpp" + +#include "testcontroller.hpp" + +#define WORDBUFFER_LEN 256 // "arbitration_active": operate in an environment without // arbitration and interrupt fielding? void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitration_mode) { // UniBone uses this test controller: // memory cells at start of IO page, can be tested with ZKMA - demo_regs_c demo_regs; + testcontroller_c testcontroller; + uint16_t wordbuffer[WORDBUFFER_LEN]; // for exam/deposit bool testcontroller_enabled = false; bool show_help = true; // show cmds on first screen, then only on error or request @@ -66,12 +70,14 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr // These test need active PRUs //TODO: rewrite. - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, arbitration_mode); buslatches_output_enable(true); // PRUCODE_UNIBUS can raise events (INIT,ACLO,DCLO) // handle & clear these - unibusadapter->enabled.set(true) ; + unibusadapter->enabled.set(true); + if (testcontroller_enabled) + testcontroller.enabled.set(true); ready = false; @@ -87,24 +93,15 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr else printf(" UniBone emulates memory from %06o to %06o.\n", emulated_memory_start_addr, emulated_memory_end_addr); - printf(" DMA uses %u%% of Unibus band width. DMA block size is %u words.\n", - unibus->dma_bandwidth_percent, unibus->dma_wordcount); if (arbitration_mode == unibus_c::ARBITRATION_MODE_CLIENT && !active) { - // Old: physical PDP_11 CPU -> test of demo_regs? + // Old: physical PDP_11 CPU -> test of testcontroller? printf("***\n"); printf("*** Starting full UNIBUS master/slave logic on PRU\n"); printf("***\n"); - if (testcontroller_enabled) { - demo_regs.enabled.set(true); - } unibusadapter->print_shared_register_map(); active = true; } - printf( - "bw bus bandwidth to be used by DMA in percent [1..100, decimal].\n"); - printf("bs DMA block word count [16..%u, decimal].\n", - MAX_DMA_WORDCOUNT); printf( "m [ ] memory range emulated by UniBone. No args = all upper. [octal]\n"); printf( @@ -132,6 +129,10 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("tr [ ] Test memory random\n"); printf("init Pulse UNIBUS INIT\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); + printf( + "dl c|s|f Debug log: Clear, Show on console, dump to File.\n"); + printf(" (file = %s)\n", + logger->default_filepath.c_str()); printf("i Info about address tables\n"); printf("< Read command form \n"); @@ -159,21 +160,10 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr iopageregisters_print_tables(); } else if (!strcasecmp(s_opcode, "pwr")) { unibus->powercycle(); - } else if (!strcasecmp(s_opcode, "bw") && n_fields == 2) { - unibus->dma_bandwidth_percent = strtol(s_param[0], NULL, 10); - if (unibus->dma_bandwidth_percent > 100) - unibus->dma_bandwidth_percent = 100; - if (unibus->dma_bandwidth_percent < 1) - unibus->dma_bandwidth_percent = 1; - } else if (!strcasecmp(s_opcode, "bs") && n_fields == 2) { - unibus->dma_wordcount = strtol(s_param[0], NULL, 10); - if (unibus->dma_wordcount < 16) - unibus->dma_wordcount = 16; - if (unibus->dma_wordcount > MAX_DMA_WORDCOUNT) - unibus->dma_wordcount = MAX_DMA_WORDCOUNT; } else if (!strcasecmp(s_opcode, "m") && n_fields == 3) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); if (ddrmem->set_range(start_addr, end_addr)) { emulated_memory_start_addr = start_addr; emulated_memory_end_addr = end_addr; @@ -196,8 +186,9 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr 0, end_addr); unibus->test_mem(arbitration_mode, 0, end_addr, 1); } else if (!strcasecmp(s_opcode, "ta") && n_fields == 3) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); uint32_t last_addr = unibus->test_sizer(arbitration_mode) - 2; if (end_addr > last_addr) end_addr = last_addr; // trunc to memory size @@ -210,31 +201,35 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("Testing %06o..%06o randomly (stop with ^C) ...\n", 0, end_addr); unibus->test_mem(arbitration_mode, 0, end_addr, 2); } else if (!strcasecmp(s_opcode, "tr") && n_fields == 3) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); uint32_t last_addr = unibus->test_sizer(arbitration_mode) - 2; if (end_addr > last_addr) end_addr = last_addr; // trunc to memory size printf("Testing %06o..%06o randomly (stop with ^C) ...\n", start_addr, end_addr); unibus->test_mem(arbitration_mode, start_addr, end_addr, 2); } else if (!strcasecmp(s_opcode, "e")) { - unsigned blocksize = 1; + unsigned wordcount = 1; unsigned i; bool timeout; if (n_fields == 1) { // auto inc addr cur_addr = (cur_addr + 2) & 0777777; - blocksize = 1; + wordcount = 1; } else if (n_fields == 2) { // examine addr 1 - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - blocksize = 1; + parse_addr18(s_param[0], &cur_addr); + wordcount = 1; } else if (n_fields == 3) { // examine addr n - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - blocksize = strtol(s_param[1], NULL, 8); + parse_addr18(s_param[0], &cur_addr); + wordcount = strtol(s_param[1], NULL, 8); + if (wordcount > WORDBUFFER_LEN) + wordcount = WORDBUFFER_LEN; } - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, cur_addr, blocksize); - for (i = 0; cur_addr <= mailbox->dma.cur_addr; i++, cur_addr += 2) - printf("EXAM %06o -> %06o\n", cur_addr, mailbox->dma.words[i]); - cur_addr = mailbox->dma.cur_addr; + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, cur_addr, + wordbuffer, wordcount); + for (i = 0; cur_addr <= unibus->dma_request->unibus_end_addr; i++, cur_addr += 2) + printf("EXAM %06o -> %06o\n", cur_addr, wordbuffer[i]); + cur_addr = unibus->dma_request->unibus_end_addr; if (timeout) printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); // cur_addr now on last address in block @@ -247,10 +242,10 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr cur_addr = (cur_addr + 2) & 0777777; blocksize = 1; } else if (n_fields == 2) { // examine addr 1 - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; + parse_addr18(s_param[0], &cur_addr); blocksize = 1; } else if (n_fields == 3) { // examine addr n - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; + parse_addr18(s_param[0], &cur_addr); blocksize = strtol(s_param[1], NULL, 8); } for (access_error = false, i = 0; !access_error && i < blocksize; @@ -262,27 +257,28 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("DDRMEM EXAM %06o -> %06o\n", cur_addr, w); } } else if (!strcasecmp(s_opcode, "d")) { - unsigned blocksize = 1; + unsigned wordcount = 1; unsigned i; bool timeout; if (n_fields >= 3) { // deposit [ ...] - blocksize = n_fields - 2; - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - mailbox->dma.words[0] = strtol(s_param[1], NULL, 8); - mailbox->dma.words[1] = strtol(s_param[2], NULL, 8); - mailbox->dma.words[2] = strtol(s_param[3], NULL, 8); - mailbox->dma.words[3] = strtol(s_param[4], NULL, 8); + wordcount = n_fields - 2; + parse_addr18(s_param[0], &cur_addr); + parse_word(s_param[1], &wordbuffer[0]); + parse_word(s_param[2], &wordbuffer[1]); + parse_word(s_param[3], &wordbuffer[2]); + parse_word(s_param[4], &wordbuffer[3]); } else if (n_fields == 2) { // deposit cur_addr = (cur_addr + 2) & 0777777; - mailbox->dma.words[0] = strtol(s_param[0], NULL, 8); - blocksize = 1; + parse_word(s_param[0], &wordbuffer[0]); + wordcount = 1; } - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATO, cur_addr, blocksize); - for (i = 0; cur_addr <= mailbox->dma.cur_addr; i++, cur_addr += 2) - printf("DEPOSIT %06o <- %06o\n", cur_addr, mailbox->dma.words[i]); - cur_addr = mailbox->dma.cur_addr; + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, cur_addr, + wordbuffer, wordcount); + for (i = 0; cur_addr <= unibus->dma_request->unibus_end_addr; i++, cur_addr += 2) + printf("DEPOSIT %06o <- %06o\n", cur_addr, wordbuffer[i]); + cur_addr = unibus->dma_request->unibus_end_addr; if (timeout) - printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); + printf("Bus timeout at %06o.\n", cur_addr); } else if (!strcasecmp(s_opcode, "xd")) { uint16_t w[256]; unsigned blocksize = 1; @@ -290,14 +286,14 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr bool access_error; if (n_fields >= 3) { // deposit [ ...] blocksize = n_fields - 2; - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - w[0] = strtol(s_param[1], NULL, 8); - w[1] = strtol(s_param[2], NULL, 8); - w[2] = strtol(s_param[3], NULL, 8); - w[3] = strtol(s_param[4], NULL, 8); + parse_addr18(s_param[0], &cur_addr); + parse_word(s_param[1], &w[0]); + parse_word(s_param[2], &w[1]); + parse_word(s_param[3], &w[2]); + parse_word(s_param[4], &w[3]); } else if (n_fields == 2) { // deposit cur_addr = (cur_addr + 2) & 0777777; - w[0] = strtol(s_param[0], NULL, 8); + parse_word(s_param[0], &w[0]); blocksize = 1; } for (access_error = false, i = 0; !access_error && i < blocksize; @@ -343,21 +339,26 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr } else { wordcount = membuffer->get_word_count(); membuffer->get_addr_range(&startaddr, &endaddr); - printf( - "Loaded %u words, writing UNIBUS memory[%06o:%06o] with blocksize %u words\n", - wordcount, startaddr, endaddr, unibus->dma_wordcount); + printf("Loaded %u words, writing UNIBUS memory[%06o:%06o].\n", wordcount, + startaddr, endaddr); unibus->mem_write(arbitration_mode, membuffer->data.words, startaddr, endaddr, - unibus->dma_wordcount, &timeout); + &timeout); } } else if (!strcasecmp(s_opcode, "s") && (n_fields == 2)) { bool timeout; uint32_t end_addr = unibus->test_sizer(arbitration_mode) - 2; - printf("Reading UNIBUS memory[0:%06o] with DMA blocks of %u words\n", end_addr, - unibus->dma_wordcount); - unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, - unibus->dma_wordcount, &timeout); + printf("Reading UNIBUS memory[0:%06o] with DMA.\n", end_addr); + unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, &timeout); printf("Saving to file %s\n", s_param[0]); membuffer->save_binary(s_param[0], end_addr + 2); + } else if (!strcasecmp(s_opcode, "dl") && n_fields == 2) { + if (!strcasecmp(s_param[0], "c")) { + logger->clear(); + printf("Debug log cleared.\n"); + } else if (!strcasecmp(s_param[0], "s")) + logger->dump(); + else if (!strcasecmp(s_param[0], "f")) + logger->dump(logger->default_filepath); } else { printf("Unknown command \"%s\"!\n", s_choice); show_help = true; @@ -370,11 +371,11 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("***\n"); if (testcontroller_enabled) { - demo_regs.enabled.set(false); + testcontroller.enabled.set(false); } active = false; } - unibusadapter->enabled.set(false) ; + unibusadapter->enabled.set(false); // Switch off bus drivers buslatches_output_enable(false); diff --git a/10.03_app_demo/2_src/menu_unibus_signals.cpp b/10.03_app_demo/2_src/menu_unibus_signals.cpp index 2eb8ce4..3dc5485 100644 --- a/10.03_app_demo/2_src/menu_unibus_signals.cpp +++ b/10.03_app_demo/2_src/menu_unibus_signals.cpp @@ -64,7 +64,7 @@ void application_c::menu_unibus_signals(void) { ready = false; while (!ready) { if (show_all) { - show_all = false ; // only once + show_all = false; // only once // display all known signals mcout_init(&mcout, unibus_signals->signals.size()); // put all panel controls into array view diff --git a/10.03_app_demo/2_src/menus.cpp b/10.03_app_demo/2_src/menus.cpp index 20be42f..c516e9c 100644 --- a/10.03_app_demo/2_src/menus.cpp +++ b/10.03_app_demo/2_src/menus.cpp @@ -44,6 +44,65 @@ using namespace std; * User Interface *********************************************/ +// octal, or '' +bool application_c::parse_word(char *txt, uint16_t *word) { + *word = 0 ; + if (!txt || *txt == 0) + return false ; + + if (*txt == '\'') { + txt++ ; + if (*txt) + *word = *txt ; // ASCII code of first char after '' + } else + *word = strtol(txt, NULL, 8); // octal literal + return true ; +} + +// octal, trunc to 18 bit +bool application_c::parse_addr18(char *txt, uint32_t *addr) { + *addr = strtol(txt, NULL, 8) ; + if (*addr > 0777777) { + *addr = 0777777 ; + return false ; + } + return true ; +} + + + +bool application_c::parse_level(char *txt, uint8_t *level) { + *level = strtol(txt, NULL, 8); + if (*level < 4 || *level > 7) { + printf("Illegal interrupt level %u, must be 4..7.\n", *level); + return false; + } + return true; +} + +bool application_c::parse_vector(char *txt, uint16_t max_vector, uint16_t *vector) { + *vector = strtol(txt, NULL, 8); + if (*vector > max_vector) { + printf("Illegal interrupt vector %06o, must be <= %06o.\n", (unsigned) *vector, + (unsigned) max_vector); + return false; + } else if ((*vector & 3) != 0) { + printf("Illegal interrupt vector %06o, must be multiple of 4.\n", *vector); + return false; + } + return true; +} + +bool application_c::parse_slot(char *txt, uint8_t *priority_slot) { + *priority_slot = strtol(txt, NULL, 10); + if (*priority_slot <= 0 || *priority_slot > 31) { + printf("Illegal priority slot %u, must be 1..31.\n", *priority_slot); + return false; + } + return true; +} + + void application_c::print_arbitration_info( enum unibus_c::arbitration_mode_enum arbitration_mode, const char *indent) { switch (arbitration_mode) { diff --git a/10.03_app_demo/5_applications/cpu/cpu20hello.cmd b/10.03_app_demo/5_applications/cpu/cpu20hello.cmd index c29b4fe..0bbdb57 100644 --- a/10.03_app_demo/5_applications/cpu/cpu20hello.cmd +++ b/10.03_app_demo/5_applications/cpu/cpu20hello.cmd @@ -6,7 +6,8 @@ dc # "device with cpu" menu m i # emulate missing memory -sd cpu20 # selected emualted 11/20 CPU +en cpu20 # switch on emulated 11/20 CPU +sd cpu20 # select m ll serial.lst # load test program diff --git a/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd b/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd index 7edc2ff..733313c 100644 --- a/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd +++ b/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd @@ -10,13 +10,19 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dk.lst -sd rk0 # select drive #0 +en rk # enable RK11 controller + + +en rk0 # enable drive #0 +sd rk0 # select p image mini-unix-tape1.rk05 # The BIN disk (RK05) -sd rk1 # select drive #1 +en rk1 # enable drive #1 +sd rk1 # select p image mini-unix-tape2.rk05 # The SRC disk (RK05) -sd rk2 # select drive #2 +en rk2 # enable drive #2 +sd rk2 # select p image mini-unix-tape3.rk05 # The MAN disk (RK05) .print Disk drive now on track after 5 secs diff --git a/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd b/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd index 4358dc6..ad5a411 100644 --- a/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd +++ b/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd @@ -8,8 +8,11 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dl.lst +en rl # enable RL11 controller + # mount RSX v4.1 in RL02 #0 and start -sd rl0 # select drive #0 +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -17,8 +20,9 @@ p powerswitch 1 # power on, now in "load" state p image rsx11m4.1_sys_34.rl02 # mount image p runstopbutton 1 # press RUN/STOP, will start - # mount disk #1 start -sd rl1 # select drive #1 +# mount disk #1 start +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -27,7 +31,8 @@ p image rsx11m4.1_user.rl02 # mount image p runstopbutton 1 # press RUN/STOP, will start # mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -36,7 +41,8 @@ p image rsx11m4.1_hlpdcl.rl02 # mount image p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -47,7 +53,7 @@ p runstopbutton 1 # press RUN/STOP, will start .print Disk drive now on track after 5 secs .wait 6000 # wait until drive spins up -p # show all params of RL1 +p # show all params of RL3 .print RL drives ready. .print RL11 boot loader installed. diff --git a/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd b/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd index 46e74ba..8f2459b 100644 --- a/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd +++ b/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd @@ -8,15 +8,18 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll du.lst -# mount RT11 v5.5 in drive #0 and start -sd uda0 # select drive #0 +en uda # enable UDA50 controller +# mount RT11 v5.5 in drive #0 and start +en uda0 # enable drive #0 +sd uda0 # select # set type to "RA80" p type RA80 p image rt11v5.5_34.ra80 # mount image file with test pattern # empty scratch disk in uda1: -sd uda1 +en uda1 # enable drive #1 +sd uda1 # select p type RA80 p image scratch1.ra80 diff --git a/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd b/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd index dea1b63..9bd29da 100644 --- a/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd +++ b/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd @@ -8,8 +8,11 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dl.lst +en rl # enable RL11 controller + # mount RT11 v5.5 in RL02 #0 and start -sd rl0 # select drive #0 +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -18,7 +21,8 @@ p image rt11v5.5_34.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount RT11 GAMES in RL02 #1 and start -sd rl1 # select drive #1 +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -27,7 +31,8 @@ p image rt11v5.5_games_34.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -36,7 +41,8 @@ p image scratch2.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" diff --git a/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd b/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd index 7898412..6d012c7 100644 --- a/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd +++ b/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd @@ -9,13 +9,18 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dk.lst -sd rk0 # select drive #0 +en rk # enable RK11 controller + +en rk0 # enable drive #0 +sd rk0 # select p image v6bin.rk -sd rk1 # select drive #1 +en rk1 # enable drive #1 +sd rk1 # select p image v6doc.rk -sd rk2 # select drive #2 +en rk2 # enable drive #2 +sd rk2 # select p image v6src.rk .print Disk drive now on track after 5 secs diff --git a/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd b/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd index ca2481f..47eb7be 100644 --- a/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd +++ b/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd @@ -1,6 +1,13 @@ # inputfile for demo to select a rl1 device in the "device test" menu. # Read in with command line option "demo --cmdfile ..." d # device test menu + +# first, make a serial port. Default ist +# sd dl11 +# p p ttyS2 # use "UART2 connector +# en dl11 + + pwr .wait 3000 # wait for PDP-11 to reset m i # install max UNIBUS memory @@ -8,8 +15,11 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dl.lst +en rl # enable RL11 controller + # mount XXDP22 in RL02 #0 and start -sd rl0 # select drive #0 +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -19,7 +29,8 @@ p runstopbutton 1 # press RUN/STOP, will start #.end # mount XXDP25 in RL02 #1 and start -sd rl1 # select drive #1 +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -28,7 +39,8 @@ p image xxdp25.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -37,7 +49,8 @@ p image scratch2.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD"