simulating or interfacing devices via an I2C bridge
(1) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-09-12 16:38:51 [link] [source]
I'm currently exploring the possibility to simulate the Type 138E/139E Analog to Digital Converter / Multiplexer so this could be used with a PiDP-8I. Given that the PDP-8 was very popular in lab environments, it would be a nice addition IMHO.
The idea is as follows: handle the respective 6 IOT commands for the 138E/139E in SIMH's PDP-8 CPU instruction decoding function by sending them (and the accumulator data) via I2C (already broken out on the latest PiDP-8 PCBs) to a microcontroller, say, the Raspberry Pi Pico (as the I2C slave). The Pico can then implement the required logic (it has 3 ADC channels and lots for GPIO pins for additional functions) and return the required info (new accumulator value and conditional branch flag) to SIMH/PDP-8. Also the Pico would have to be notified via I2C for boot-and power-up events I guess, so there would be mainly 2 or so places in SIMH that would be affected by a change.
So I wonder whether, from the SIMH / PiDP-8/I software point-of-view this would be the "right" way to do it.
One could even think this a bit further and have a "generic" I2C bridge "fallback"-device that offloads any IOT operations not supported by internal SIMH devices to a connected I2C slave device (should be useful for prototyping and connecting to "physical" devices like DIY paper tape readers etc...). I guess anything that is not too high speed and doesn't require interrupt logic could be emulated with rather minimal programming on the Pico side.
Thoughts?
Thanks in advance, HBE
(2.1) By Warren Young (tangent) on 2021-09-29 13:16:42 edited from 2.0 in reply to 1 [link] [source]
simulate the Type 138E/139E
I tried searching for manuals for these, but I couldn't find anything useful. Whether that's because each of the key words are too common or because there just isn't much out there, my web-fu has failed me. In absence of hard data, I'm left with many questions.
Among the docs I tried and failed to find is a BOM showing what a LAB-8/e adds to a PDP-8/e. My sense is that there was, at least in principle, a list of items you could order off the DEC price list to assemble something functionally equivalent to a factory-produced unit; what is that list, and where are each of the items' manuals? Are these two peripherals part of that complement? If so, then we already have a wishlist item for that, so we should either be couching the conversation in those terms or updating that ticket to bring it into alignment with your plans.
If not, and these peripherals you wish to emulate are something else entirely, we need to discuss instead whether it's worth going on a left-turn relative to the prior plan: replace the ticket, allow future forked efforts between the versions, etc.
A related document hosted here is the AX08 plan.
Since there were so many official "lab" PDP-8 variants plus ways to turn a normal PDP-8 into something similar and so few active hobbyists interested in ongoing development, I doubt we have enough total attention to maintain more than one of these projects, even if you get others actively interested. If you agree, we should start by answering "Which one?" and then stick to that. In the SIMH spirit, it should be the most capable one so we can cover the most use cases.
That said, why the LAB-8/e rather than something more contemporaneous with the PDP-8/I? If we can find an esthetic match, that opens the door to a PiDP-8/I LAB peripheral that looks like it belongs to it rather than a naked PCB hanging off the side.
the Raspberry Pi Pico (as the I2C slave).
Does it have enough GPIO? An Arduino has twice the ADCs, but fewer digital lines and fewer PWMs, which might end up being a better tradeoff. Note that the AX08 has 4 ADCs, but the Pi Pico has only 3, so there's a mismatch right there. What's the I/O complement of the 138/139 pair? What of the LAB-8/e ADC boards?
I'm not trying to arm-twist you into going with an Arduino. I believe the BeagleBoard line also has a lot of I/O. The point is, pick something that has enough to cover what you need it to accomplish.
Broadly, though, yes: this is something that should be done with a bare-metal development board to achieve proper real-time I/O. Linux should be nowhere involved in this piece.
the Pico would have to be notified via I2C for boot-and power-up events I guess
Lacking proper docs, that isn't evident to me from the vague marketing descriptions I can find. Did the real hardware react in some way when the host CPU rebooted out from under it? My overall sense of PDP-8 era hardware is that peripherals just went on doing their peripheral thing when abandoned like this, so it took another hardware command for them to do something new. When the host CPU's OS came up, it'd give such an instruction eventually.
from the SIMH / PiDP-8/I software point-of-view this would be the "right" way to do it.
Sure. SIMH is designed to have software peripherals added to it. So long as you go through the supported interfaces and provide code that doesn't break people's builds (e.g. by requiring exotic libraries) you should be able to get the resulting features upstreamed.
Having accomplished that, syncing the changes back down is a one-command process if the merge is clean, which it should be given that you've probably done the original work in the PiDP-8/i software tree before upstreaming it.
a "generic" I2C bridge "fallback"-device that offloads any IOT operations
If you mean "device" in the SIMH sense, then yes: the protocol you build over I²C should allow further devices on the same bus to catch different IOT commands. This is the point of a bus, after all, and it matches how the PDP-8 busses worked: whichever peripheral card knew how to catch the IOT command, that's the one that responded.
If instead you're suggesting that we have one monstrous PCB hanging out there that handles everything over a single connection, then that's madness. Even if you had the developer time to create it, there's far too much PDP-8 hardware, much of it mutually incompatible, for a single complicated device to handle it all.
anything that is not too high speed
My gut sense is that whether you go with USB, I²C, or anything else sufficiently modern, it'll swamp what the PDP-8 busses could manage. My ballpark guess is something well under a megabit, maybe in the hundreds of kbit/sec total I/O.
But do the math and get it right from the start. There's no sense in getting partway into the project and crying about being out of bandwidth.
(3) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-09-29 15:22:25 in reply to 2.0 [link] [source]
Thanks for the many useful thoughts on this.
I was unaware of the AX08 idea, that sounds really interesting.
I picked the Type 138E/139E ADC/MUX because it was really simple yet useful, you can find documentation in the Straight-8 Handbook, e.g. here : http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/pdp8/pdp8/F-85_PDP-8_Users_Handbook_May66.pdf
Given that it was intended for the Straight-8, would you have been able to use it in an 8I (with level shifters perhaps..)??
About the Raspberry Pico:
Does it have enough GPIO? An Arduino has twice the ADCs, but fewer digital lines and fewer PWMs, which might end up being a better tradeoff. Note that the AX08 has 4 ADCs, but the Pi Pico has only 3, so there's a mismatch right there. What's the I/O complement of the 138/139 pair?
Indeed the 139E supported up to 64 (!)analog channels, something neither the Pico nor any of the popular Arduino boards I know of will "natively" handle, but then again the Pico has a second I2C port (as well as SPI) so you can attach many more ADCs to it if you wish. But for starters 3 channels are ok for me. It will be hard (using I2C) to match the speed of the 138E which could do > 10 k samples /sec even at the full 12bit resolution, while the Raspberry Pi that SIMH is running on is usually running the I2C bus at up to 400kbits /s (fast modes are in principle supported but not recommended).
The more I think about it and after considering your feedback, I wonder if the best way indeed is to keep SIMH as much out of the picture and "just" have a new, lean 'i2cbridge' SIMH device that could be like this:
Parameters:
- IOT device range (default could be 030 .. 037, apparently reserved for user-built applications, using DEC supplied flip-chips of course as building blocks )
- I2C target device (the I2C address of an I2C slave handling any IOT commands for devices in the above range)
So this SIMH-device would then just forward the IOT command (device no, "pulse") and the AC (all in all 3 bytes) to the I2C device with the configured address and read back its response (a flag to show whether the next ins should be skipped and a new AC value, 2 bytes in total). All the logic is then handled on whatever you want to connect as the I2C slave: Pico, Arduino, ... .
To make things more flexible you should be able to configure more than one pair (IOT device range, I2C target address) to put more than one device on the bus and support more than one IOT-device-number-range.
With such a SIMH device, people could start immediately implementing all sorts of interfaces to sensors or actuators, many of them have good example code for Arduino or MicroPhython/CircuitPyhton anyway, on the microcontroller of their choice.
This doesn't handle DMA or interrupts, but I think it would still cover a useful subset of what people could do using the PDP8 in lab-like environments.
I'm currently trying to put a demo application together that reads a 2 axis joystick (+ 1 button) , but I guess I first have to learn a bit PDP-8 assembly to make a little read-out loop :-)
Cheers HB
(4) By Warren Young (tangent) on 2021-09-29 16:10:24 in reply to 3 [link] [source]
the AX08 idea…sounds really interesting.
I should clarify something: while I expect only one of these ideas to survive to full fruition, if any appear at all, I'm not closing tickets or deleting wiki articles for the other ideas until one dominant implementation does appear. This project is a do-ocracy: he who does the work makes the rules. Whichever project appears first is likely to gather enough momentum to obviate the others, provided it covers a sufficiently broad set of use cases.
On the chance that this implementor turns out to be you, I offer you this advice: you've got a list of at least four different alternatives here in this short thread alone, and I assure you there are several others, so you should go through them all and select the one you wish to emulate.
It could be that someone else comes up with a competing one-off, but I think the Nash equilibrium of such an ecosystem is beyond 90/10. One implementation will steamroll the others, if there ends up being more than one.
you can find documentation in the Straight-8 Handbook
That explains my failures: I assumed from the "E" suffix that these were PDP-8/e peripherals and so was using that as a keyword to narrow the billions of results that "lab & ADC & digital" brought up. DEC would never survive with such bland product names in today's marketplace.
you can attach many more ADCs to it if you wish
Sure, I realize that. It's just that with so many development boards to choose from, I expect there's one out there that covers the needed I/O in its entirety, right out of the box. The shorter your BOM list, the more people you'll get to build it, so the more people you'll attract to your project, so the more likely it will survive to be The One.
10 k samples /sec…12bit resolution…400kbits /s
You're on the right track there, but realize that even the fastest PDP-8 CPUs could only run ~500 kIPS, and then only for in-CPU instructions like TAD. The IOT instruction is the slowest type, and I would not be surprised to find more than just conversion delays within these peripherals.
(I'm blindly guessing rather than RTFMing since I doubt the 138/139 is your best emulation target consequent to the above concerns.)
I highly doubt a single PDP-8 could sample all 64 channels at full resolution at full speed. Applications using even a large fraction of the available channel count would surely have sampled at a lower rate.
Given that 500 kIPS and 400 kbit/sec are of the same order of magnitude, I expect you can't run PDP-8 programs at native instruction rates and still swamp the Pi's I²C bus, provided protocol overhead is reasonable.
lean 'i2cbridge' SIMH device
It is your privilege as implementor to choose your own design.
However, do realize that if you're using I²C as a transport between the CPUs, it's still a real-time task to receive the other board's data, so the more layers you insert the more chance you'll have of dropping packets. The most recent Embedded podcast had one of the hosts telling a story of this occurring on a Linux + Python system with a CAN bus, in fact.
You'll either want to have a real-time thread that does little more than catch packets and queue them for the SIMH CPU decoder to pull from, delivering them into the simulation, or you'll want to switch to something like TCP/IP over Ethernet so the OS's network stack will do this queueing for you.
That latter option has certain attractions, such as the ability to place the ADC board near the point of measurement while the PiDP-8/I's switches and lights remain close at hand, across the room, across the building, or across the world.
The main downside is that while TCP/IP is easy on the Pi side, it adds a lot of complexity to a bare-bones development board. Even where available, such stacks are often pretty crappy, compared to what you get in Linux.
(5) By Bill Cattey (poetnerd) on 2021-09-29 21:21:23 in reply to 4 [link] [source]
A project I keep wanting to take on is, "Code for SIMH to configure for the different PDP-8 Family members."
What we currently have is SIMH that implements the PDP-8/e but in a box that is ostensibly a PDP-8/i. The instruction sets are subtly different. Indeed the extended arithmetic element for the two processors is VERY different. (The PDP-8/i EAE does not have Mode B instructions.)
The documentation for peripheral interfaces relevant to the PDP-8/i would be the 1970 Small Computer Handbook. I serve that up from poetnerd.com at: https://poetnerd.com/pdp8-alive/pdp8-alive/doc/trunk/doc/Hardware/Small-Computer-Handbook/1970/SmallComputerHandbook_1970.pdf
The relevant document for the PDP-8/e would be the 1973 edition that can be found at: https://poetnerd.com/pdp8-alive/pdp8-alive/doc/trunk/doc/Hardware/Small-Computer-Handbook/1973/Small_Computer_Handbook_1973.pdf
For a little summary of the different editions, see: https://poetnerd.com/pdp8-alive/pdp8-alive/dir?ci=tip&name=doc/Hardware/Small-Computer-Handbook
Although I don't plan on working on this particular peripheral, I'd suggest picking either the 8i or the 8e devices for emulation. Although the 8i would be more "correct" for the PiDP-8/i box, work done in SIMH for 8e devices, and other software might be of broader community interest. I say this because I presume (admittedly without a lot of evidence) that there is wider ownership of 8e hardware than 8i hardware. Also because the current code line of SIMH is really PDP-8e specific, and lacks awareness of the 8i differences.
In passing I will note that I believe SIMH for the PDP-11 does have config settings for different members of that family.
(6) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-03 21:13:18 in reply to 5 [link] [source]
So there are three different project ideas in this thread already:
Extend SIMH to support multiple PDP-8 family members, not just the 8e
Extend SIMH to simulate a particular piece of lab equipment
Extend SIMH to implement a generic "I2C bridge" device that forwards IOT commands for configurable PDP8 device numbers to a configurable I2C device on the host Raspberry Pi I2C-1 interface, and react accordingly to the response.
I will only have time and motivation to do 3), and when I'm done I guess people here can judge whether the result is worth being included in the PiDP-8i repository. My goal is to have something by early Dec latest that will a) allow me to replicate https://youtu.be/B1MDYYvw0cY and play this https://github.com/Roland-Huisman/SpaceWar with "joysticks".
Cheers HB
(7.1) By Warren Young (tangent) on 2021-10-04 17:41:11 edited from 7.0 in reply to 6 [link] [source]
I guess people here can judge whether the result is worth being included in the PiDP-8i repository
If you get it upstreamed, we'll bring it back down into our tree naturally.
There are features of the SIMH PDP8 simulator we don't compile into the stock builds, but that's generally because they don't make sense in the PiDP-8/I physical realization, like the video stuff, which is often not exposed outside the case, and when it is, isn't always used anyway.
The 3 lines of an I²C bus won't count the same way; drilling a small hole for a cable isn't nearly as difficult as porting the HDMI out in a way that doesn't mangle signal integrity, so there won't be any good reason not to compile that feature into the distribution.
(8) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-06 17:10:06 in reply to 7.1 [link] [source]
Not sure this is interesting enough for upstream SIMH because the way I envision it it will be I2C only and that doesn't apply to most host platforms that SIMH is running at, and it would be (for the moment) PDP-8 only.
HB
(9) By Warren Young (tangent) on 2021-10-07 07:53:58 in reply to 8 [link] [source]
There's a generic I²C interface on Linux. There may be worthy abstraction libraries for other platforms as well.
I don't know what the Raspberry Pi OS SDK provides, but I should hope it rides atop this. You certainly don't want to be bit-banging with your own userspace driver.
(10) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-07 23:23:08 in reply to 9 [link] [source]
Sure but the hardware that SIMH is running on under Windows, Linux, BSDs , OpenVMS or OSX will rarely expose an I²C bus. The Raspberry Pi is the exception here. So I'm not sure this is useful for many SIMH users other than those who would also be able to run the PiDP-8 software.
HB
(11) By Warren Young (tangent) on 2021-10-10 00:17:20 in reply to 10 [link] [source]
I've given your account commit privileges. Please familiarize yourself with the advice in the contribution guide. That will help you set up a working branch to develop this idea on. Keep in mind, it doesn't have to function properly or even compile until it gets merged down to trunk. A development branch is a collaboration tool, not a way to present finished perfect ideas. Experiment, develop, play.
Above all, realize that you're the one doing the work, so within the project's overarching rules, you set the rules for how this feature works and what you want it to do.
Welcome to the project!
(12) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-10 12:24:57 in reply to 11 [link] [source]
Excellent, thanks! Now I need to study fossil a bit.
I hope I'll have something to at least reality-check the idea e.g. for benchmarking the throughput and testing robustness soonish.
HB
(13) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-16 13:55:45 in reply to 12 [link] [source]
OK, so I have started work on a new i2c-bridge-device branch.
As this is my first contribution to PiDP8 and my first contact with fossil, I'll commit frequently and if one of the gurus here happens to spot me doing something weird, please don't hesitate to let me know in this thread. I hope this doesn't create email spam for someone.
Cheers HB
(14) By Warren Young (tangent) on 2021-10-18 00:51:09 in reply to 13 [link] [source]
With the understanding that I have not run it nor do I have any intentions of doing so, I'll restrict my comments to matters of style:
Run the code through
tools/restyle
. I'd do this for you myself, but it makes so many changes that if I checked the changes it makes in, the output of commands like "fossil blame" would give the false impression that I wrote the whole module.Although the style guide in
CONTRIBUTING.md
says you should follow the oddball SIMH style for code that's part of SIMH, this is a standalone file, so since you aren't going to try to upstream it, you can use the more mainstream code style rules implemented intools/restyle
.Or, keep with the SIMH style, if you don't find that objectionable.
Whichever option you pick, the point is, pick one of these two styles; don't add a third style to the simulator code.
Before you make commits, you should run a diff on them to make sure they don't change things they shouldn't. This general principle came to mind when I found the change at the end of this commit. Note the pointless whitespace change on the "else" line. Since this is an upstream SIMH file, allowing such changes to slip in can create a merge conflict when we go to update our version of SIMH. I've backed this unintentional change out for you.
I'm a fan of vertical whitespace in source files, but I think you've gone a bit overboard in this file, particularly with the 3- and 4-line blanks between functions. Would you please tighten it up a bit? 2 blank lines I can live with. Otherwise, the contribution guide's rule applies: "When in doubt, mimic what you see in the current code."
Change all of the "Robert M. Supnik" stuff up in the license comment at the top of the file to your name. It's your code, so it's your automatic copyright under the Berne Convention. By applying this comment to the file, you are licensing it in a way that allows us to redistribute it. Without this license grant, I'd have to roll your contribution back, but as it stands, it's legally bogus: Robert M. Supnik wasn't involved in its development, so he can't grant us the license we need.
I could do this for you, but then it would be me relicensing your code, which is just as dodgy. The commit making the change needs to come from you to be legally kosher.
Add your name and a brief description of your contribution to the bottom of
AUTHORS.md
, above my catch-all entry.Add a
doc/i2cb.md
file describing how to use your new facility.Check my new ChangeLog.md entry for correctness, and to ensure that it summarizes the feature's purpose and scope sufficiently. It shouldn't try to compete with the full doc, but it should give readers an idea of what they can find in that doc, and what use they may get from the feature.
Before we merge the branch to trunk, I'd like to have all of the TODOs resolved, one way or another. If there are outstanding TODOs at the time you decide it's sufficiently complete to merge to trunk, those items should move into feature tickets. ("Wishes" in the button bar above.) I've created a new "I²C IOT bridge" entry in the "subsystem" list for such things.
One TODO in particular I'm concerned with: the hard-coded
-DI2C_BR_DEVICE
bit inMakefile.in
will have to go before this can be merged to trunk. It should be a--with-i2c-bridge
type option to./configure
. Such changes go intoauto.def
, where you can also do things like detect the availability and usability of the I²C abstraction library you're using.
Other than that, I have no comment. 😜
(15) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-18 06:55:46 in reply to 14 [link] [source]
Thanks for the comments, all very useful.
I was wondering about the "Robert M. Supnik" copyright comments, as every single module in the PHP8 part contains this remark even tho some of the modules must have been contributed by other authors. The new module is leaning heavily on other modules so if one searched for it, you'd find common code here and there so I think at least partial credit needs to be given to the SIMH project, but we can do that at the end of the header perhaps as an acknowledgement.
Before we merge the branch to trunk
Oh I think it's still a long way before this is ready for merging, this is rather experimental still. Especially the error handling needs to be improved. And maybe there is a way to allow to handle interrupts via the free GPIO19 pin.
One TODO in particular I'm concerned with: the hard-coded -DI2C_BR_DEVICE bit in Makefile.in
Yes, as mentioned in the C file, this needs to be handled in configure. The whole code is written for the Raspberry Pi under Raspbian at the moment and it needs to be disabled for anything else. The rather thin but useful wrapper library (under LGPL) around I2C I/O could perhaps be included in the build itself and statically linked, but I'm not sure that this matches the author's plan wrt. SIM/PDP-8, that project originated for the PiDP-11. Otherwise we would want to check for the library as a dependency in configure, sure.
Cheers HB
(16) By Warren Young (tangent) on 2021-10-18 13:49:57 in reply to 15 [link] [source]
On the copyright issue, I can't affect how the upstream project manages their legal policies. I can with this one. :)
If you think your new module is a substantial derivative of existing code, then the solution is to declare both copyrights:
Copyright (c) 1993-2016 by Robert M Supnik, and (c) 2021 by Heinz-Bernd Eggenstein
The year range in the first part comes from the module you derived yours from.
(17) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-18 14:19:20 in reply to 16 [link] [source]
I like that, will do so with the next substantial commit.
Thanks hb
(18) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-27 17:46:41 in reply to 17 [source]
Some progress doing benchmarks: When raising the I2C clock frequency on the Raspberry Pi 4 to a still modest 400kHz, you can offload simple IOT command to a Raspberry Pico at a rate of 5000 + IOT commands per second. This is a far cry from the native PDP-8i instruction per second rate, but I guess it's ok for "recreational" use. I hope top have a demo ready for Halloween ;-)
But what I do see is more visible "flickering" of the ILS driven LEDs. Lately there has been discussions (even a suggested patch) on the flicking in certain situations here: https://groups.google.com/g/pidp-8/c/PNS6YyOCoAE/m/M7MX6KGfAwAJ but I'm not at all convinced that the "dithering" in the sampling of LED status is the (only) culprit. I'd rather try a fix along what is suggested in this bug: https://tangentsoft.com/pidp8i/tktview?name=b8d9865f31
I'm not quite sure whether that should/must go into a separate branch then?
HB
(19) By Warren Young (tangent) on 2021-10-27 21:24:55 in reply to 18 [link] [source]
...400kHz...5000 + IOT commands per second
So, 10 octets per command?
This is a far cry from the native PDP-8i instruction per second rate
Keep in mind that the IPS rates on my throttling doc speak of TAD instructions, not IOT instructions. Actual IOT commands took more than the 2 cycles of a TAD, and you can't usefully run IOT commands back-to-back anyway, if only because you need other intervening instructions to move the resulting data in/out of core or to process it or whatever.
What's the rate an actual PDP-8 could run IOT commands, and how does that vary by device type?
I'm not at all convinced that the "dithering" in the sampling of LED status is the (only) culprit.
The dithering is on purpose, being a perfectly standard way to break up aliasing artifacts inherent in the nature of the sampling interface between the CPU and ILS threads.
The only way I can see to get to zero dither in the general case is 1:1 instruction sampling, which I've tried, at great cost to performance, roughly an order of magnitude.
even a suggested patch
It suffers the same problems Ian's solutions to the ILS have always had: it works on only one CPU type and at one throttle rate. In this case, it's due to use of a hard-coded sample rate constant. Change the IPS rate either by running it on a different CPU or by throttling the simulator and you get a different visual effect.
I think he's at least got the prime number on the right side of the division sign to produce the desired effect this time (contrast Ian's original ILS) but for this technique to work in the general case, you need to pick a different prime depending on the intended IPS rate.
I'm also annoyed by the fact that this patch reverts the intentional MB change, done as a result of Bill Cattey's visit to the RICM to study an actual PDP-8/I. That change was motivated by people bitching about MB not being handled correctly, so now it's changed and people are bitching again. DEC's own docs disagree about what the actual machines did, so at this point I've given up until there's physical evidence of actual behavior on offer, since I can't tell claims of incorrectness from bikeshedding.
I think someone needs to sit down with the two machines side-by-side and exercise the heck out of them.
I'm not quite sure whether that should/must go into a separate branch then?
Since all of this looks subjective and unlikely to result in a single commit that gets everything unconditionally correct, I don't see how you can commit directly to the trunk.
Do be aware that you can merge between branches, so you could commit Ian's latest ILS hacks to one branch and then merge it into your I2CB branch for testing the pair in combination. Yet, you should not commit the merge, since they're orthogonal issues until both are settled and can merge to trunk, and thence eventually to a release.
(20) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-10-27 22:21:57 in reply to 19 [link] [source]
So, 10 octets per command?
The "protocol" actually sends 3 octets of payload and receives 2, the rest is some "overhead" ( like the bus address and flow control stuff, but both sides were running C, no Python here. So I think by and large what could be expected.
What's the rate an actual PDP-8 could run IOT commands, and how does that vary by device type?
Good question, but my naive expectation was that IOT commands never take longer than just a few cycles. Device actions that take longer must then be done in hardware after the IOT completes and the user's software will be notified by either an interrupt or by polling with an "Are we there yet???"-type-IOT. But again, this whole I2CB feature is for recreational stuff and prototyping, it doesn't claim to emulate a real device. You could probably still use this to connect a DIY paper-tape-reader done with LEGO technic etc :-).
patch
Yeah I'm reluctant to invest time in it at the moment because there are some seamingly unrelated things going on in this patch and I'm not sufficiently convinced (yet) about the merrits, no offense, but you mentioned a few valid points. Maybe later. I would prefer to first try out the "do not zero out the LED states when double-buffereing but do a copy, then overwrite all LEDs (on or off) in the update"-idea that is mentioned in the bug that I cited above. Any additional dead-time of the LEDs cannot be good, at any throttling rate.
I think someone needs to sit down with the two machines side-by-side and exercise the heck out of them.
Indeed. I actually just yesterday stumbled over an internet site of a privately run museum here in Germany that claims it lets visitors "touch" their stuff (under supervision I guess :-) ) but I'm not sure whether the machines are in a good operating condition. There are photos of (their?) PDP-8i in action tho, so there is some hope. I emailed them whether it would be possible to have short "hands-on" sessions with their PDP-8i, running some short experiments. And I'm totally willing to support them with an adequate donation. I'll let you know when/if I get a reply. I guess if PiDP-8i software could have a hands-on timeslot say once every quarter it would already be very useful. It's a bit complicated because the original collector/curator of that place passed away not too long ago.
branches
Yeah I think I will keep things in separate branches then (the limititation of the manual operations on the first 4096words issue, the I2CB stuff and the flickering issue which can be reproduced without the I2CB thing). Should all be pretty separate.
Cheers HB
(21) By Warren Young (tangent) on 2021-10-28 00:00:25 in reply to 20 [link] [source]
IOT commands never take longer than just a few cycles.
Each device will be different, but here's a relevant example from the 1973 Small Computer Handbook (pp.6-96 thru 6-100) for the PDP-8/e family and its AD8-EA 10-bit ADC peripheral:
- 20 µs conversion time
- 3 µs sample acquisition
For the PDP-8/e, that's about 19 core memory cycles at 1.2 µs each.
Furthermore, you have to then run the assembly to acquire the sample:
ADST /Clear the ADC done flag and start conversion
ADSK /Skip the next instruction when done
JMP —1 /Jump back one instruction
ADRB /Read ADC buffer into AC
…presumably followed by a TAD or similar to do something with the AC. The first instruction will start that 20 µs conversion time, and I believe the remaining 3 µs is in that final instruction.
The times vary by device. For a spinning rust disk, the RPM of the platters and the seek time of the heads (if not fixed) affects the IOT instruction time.
the user's software will be notified by an interrupt
PDP-8 interrupt handling is super-primitive, and OS/8 doesn't use the facility anyway.
by polling with an "Are we there yet???"-type-IOT
Yes, that's how OS/8 handles things when it does the device I/O and how most normal user software handles things as well. However, I'm charging that polling time against the cost of the instruction. It's not like we've got multiprocessing going on here. While the CPU is sitting in a loop polling a flag like in the code above, it isn't doing anything else.
I suppose it affects your module because you have to all ~25 of those IOT calls down the I²C pipe if you're using it to emulate an AD8-EA, most of which are wasteful polling instructions that chew up available "bus" bandwidth.
there are some seamingly unrelated things going on in this patch
Yes. I'm pretty sure you can drop about the last quarter of that patch and not affect whether it does what Ian wants to demonstrate. Alas, this isn't the first time he's posted a dirty patch.
(22.1) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-11-07 10:27:53 edited from 22.0 in reply to 21 [link] [source]
So I'm looking into reducing LED flicker when the instructions-per-second throughput is low because of many IOT-over-I2C transactions, and stumbled over this part of the ILS code:
const size_t inst_count = pdis_paint->inst_count;
size_t br_quant = inst_count >= 32 ? (inst_count >> 5) : 1;
for (int row = 0; row < NLEDROWS; ++row) {
size_t *prow = pdis_paint->on[row];
for (int col = 0; col < NCOLS; ++col) {
br_targets[row][col] = prow[col] / br_quant;
}
}
I wonder if this is actually the right way to do it, let's look at two examples:
inst_count= 62 prow[col] = 41 , so that LED was lit 2/3 of the time
but the code will compute: br_quant = (inst_count >> 5) = 62 >> 5 = 1 (!)
prow[col] / br_quant = 41 / 1 = 41 which means max intensity (actually higher than that if it could...).
For a just slightly different situation, the outcome is more correct:
inst_count= 65 prow[col] = 42 , so that LED was again lit 2/3 of the time
the code will compute: br_quant = (inst_count >> 5) = 65 >> 5 = 2
prow[col] / br_quant = 42 / 2 = 21 which is what you would expect
The error vanishes when inst_count gets much larger than 32, but then again the code is actually trying hard to limit the number of rather expensive updates of the display buffer by calibrating the refresh rate every second or so. So I think this code should better work also for rather small numbers.
I see there is an effort to avoid floating point math here so an alternative formula could be
target_brightness = (prow[col] << 5) / inst_count
I'm also not sure if it is necessary to update the brightness values (applying the ramp-up/ramp-down factors to compute the current brightness from the current PWM brighness and target brightness) within each of the 32 steps of a PWM cycle, wouldn't it be more logical to do that only once per PWM cycle when the target brighness changes?
(23) By Warren Young (tangent) on 2021-11-07 18:05:07 in reply to 22.1 [link] [source]
when the instructions-per-second throughput is low because of many IOT-over-I2C transactions
It occurs to me that your 5000 IPS number from up-thread is indeed a problem here. The ILS tries for 100 updates per second, which means there's only 50-ish instructions in a tight back-to-back loop of transmitted IOT instructions, as in my ADC example above. Add in the occasional fast JMP instruction, and that's where you get numbers like the "62" you're showing in your example.
With polling-driven peripherals like this, 400 kbit/sec I²C is too slow. The ILS is programmed with the assumption that it will be running something on the order of hundreds of thousands of IPS minimum if you've got the simulator throttled to match actual hardware.
Given the inference above that each IOT costs about 10 bytes with your protocol, and then adding 40 bytes of overhead per TCP/IP packet, you can match to the hardware's performance over BSD sockets with no more than a 16.65 Mbit/sec connection. If you permit the Nagle algorithm to work by e.g. coalescing a "send" type IOT command followed by a poll, you might even get that under 10 Mbit/sec for practical code. That gets you into WiFi territory, though for the purposes of this subthread — stability of the front panel display — you'd be better off with a wired connection.
The error vanishes when inst_count gets much larger than 32
Yes: it's written with the assumption that instructions per display update is no lower than about 100 times that if you're using the ILS. If you have the simulator throttled to match the PDP-8/I, the IPS rate is something like 333 kIPS, so 100 updates per second means 3300 instructions per update.
Even with the poor PDP-8/S clock rate, 100 updates per second means ~620 instructions have been executed between each update, a factor of ~20.
The problem isn't the math, it's that your I²C bridge is about one twelfth the speed of a PDP-8/S.
I see there is an effort to avoid floating point math here
Not precisely. All of the Raspberry Pi CPUs have hardware FP, and an earlier version of the ILS did indeed use FP math, just to see what would happen, and it didn't hurt performance much. However, since it didn't help, either, we went with integer math.
The problem isn't FP versus int, it's that 100 updates per second divides 5000 IPS too coarsely.
wouldn't it be more logical to do that only once per PWM cycle when the target brighness changes?
I've just skimmed the code, but isn't that precisely what it's doing inside the "step == MAX_BRIGHTNESS
" branch?
(24) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-11-08 12:18:58 in reply to 23 [link] [source]
I don't quite agree on some points:
Yes: it's written with the assumption that instructions per display update is no lower than about 100 times that if you're using the ILS. If you have the simulator throttled to match the PDP-8/I, the IPS rate is something like 333 kIPS, so 100 updates per second means 3300 instructions per update.
Even with the poor PDP-8/S clock rate, 100 updates per second means ~620 instructions have been executed between each update, a factor of ~20.
That all would be true if the LED state statistics were updated (set_pidp8i_leds) after each single instruction, but pidp8i deliberately samples only every max_skips (minus some random dither value) instructions , which brings the actual instructions counts used for each of the ~100Hz panel updates closer to the regime where the numerical error done by rounding the denominator in the "fraction of time this LED was on" formula does indeed matter, as in my example. The code is self-adjusting max_skips every second so that throttling, and even moderate use of slow IOT-over-I2C instructions here and there should not matter as long as the IPS remains above ca 3.2k ips. At 1MHz I2C clock (I tried this and it still gives good signals if you pay attention to your pull-up-resistors) this is very realistic.
So I really think the said rounding should be done differently, or the instruction skipping done less agressively.
I've just skimmed the code, but isn't that precisely what it's doing inside the "step == MAX_BRIGHTNESS" branch?
Actually not IMHO. The code that updates the BRIGHTNESS_TARGET for each LED is inside the "step == MAX_BRIGHTNESS" branch. The code that takes the current brightness of each LED and evolves it over time to converge to the current BRIGHTNESS_TARGET is outside that branch and is executed for all the 32 (31?) steps of the "PWM" cycle. There might be very subtle effects on the decay / rise curve by this but I see no real point in changing the brighness of the LEDs in between "PWM" pulses, currently that logic is anyway a bit buggy because of https://tangentsoft.com/pidp8i/tktview?name=135d14a0c2 so perhaps one could fix all of that in one go.
But I could be wrong with all this, I'm quite new to this code and the current test version that I play with to understand the ILS mechanism (without I2C traffic, so this is an entirely different story) seems to give me a ~ 22.5 Hz refresh rate only instead of 100Hz (this is actually measured with an oscilloscope and photoresistor to capture all the effects (hardware and software, even ripples in the power rails and slowness of the LED driver chip etc)). This measurement is fast enough to see the individual "PWM" cycles and is the best way to capture the human perception of any flickering, I guess. But I'm not yet understanding everything that I see.
(I put PWM in quotes here and there because as far as I understand the code, it's not actually Pulse-Width-Modulation that is used, all the pulses seem to have have equal width, it's more like Pulse Frequency Modulation, right?)
(25.1) By Warren Young (tangent) on 2021-11-08 18:41:57 edited from 25.0 in reply to 24 [link] [source]
after each single instruction
If you do that, the 7-8 MIPS benchmark on a Pi 3 (CPU thread unthrottled) drops to ~1 MIPS, because it literally takes ~8x the CPU time to run the ILS code as to decode instructions, on average. That's why we have the instruction skipping logic.
But I could be wrong with all this
I'm not trying to debate it, but to explain what the new ILS does and why I wrote it that way so you don't retread old ground. I'm not fighting to keep it as it is, just making sure you don't go haring off into bad ideas I've already tried and rejected.
I have very little interest in active development on this project at the moment. I'll continue to host it, but it's up to people like you still interested in making changes to push it forward. If you want to try and replace the ILS, go right on ahead. All I ask is that you understand what it's doing and why before you rip it up and try to reinvent it.
One thing I think you aren't taking into account: a feedback-based control system like this one can't react quickly when the system is jerking back and forth between millions of IPS and thousands of IPS. You've put me in the position of a car mechanic listening to the driver complaining that the speedometer needle doesn't move smoothly when he sets the car's cruise control system to 100 kph while it's towing a trailer that permits the car's engine to go only 1 kph while the tachometer is red-lining. The thing is, this is a mystical trailer that appears and disappears, so you absolutely bet the speedometer needle will swing wildly back and forth each time the trailer appears or disappears.
To make the ILS work sensibly in this scheme, I think you'll have to throttle the CPU to something close to the IOT bridge's speed, ~5 kIPS. At that point, sampling every instruction would be sensible, because you'll have plenty of host-side CPU power to spare between instructions.
We may have two entirely separate issues here: if you bring the bridge speed up to the point that it can issue millions of IOT instructions per second, you may still find that the ILS could use work. Chasing both issues at the same time is likely to lead to confusion.
Or, maybe the current ILS is fine, but you need a third mode, one meant to run at these slow IPS rates.
it's more like Pulse Frequency Modulation, right?
Not as I recall it. It's supposed to decide how much "on" time to give each LED based on the prior decoded instruction buckets, which results in PWM.
(26) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-11-08 22:50:17 in reply to 25.1 [link] [source]
I think we are still not on the same page. I agree it is reasonable not to sample the LED stats every instruction, but the fact that the code does sample only every now and then (dithered) does mean that the way the rounding of the duty cycle works in the current code is in a regime where the results can be plain wrong so it's best to fix this.
The following issues are also independent of the I2C bridge thingy and how the code's feedback loops adjust to it.
Issue 1: The "current brightness" values are recomputed between PWM pulses, which I think is a waste of cycles
Issue 2: on my Pi3A+, the refresh loop works at only ca 22Hz which results in annoying flicker (and it even has its CPU freq. fixed to 1.4 GHz via "force_turbo=1" in its boot config). It is instructive to look specifically at (even measure) the brightness of the "FETCH" and "EXECUTE" LEDs, because during normal operation their target brightness is hardwired in the code to 50% so these particular LEDs are completely decoupled from what ever happens in the instruction decode cycle and whatever could go wrong there (I2C stuff, dithering, feedback loops , whatever). When those LEDs flicker visibly it is because the display update code isn't executing fast enough (say < 40 Hz).
Issue 3: The current code actually does Pulse Frequency Modulation, not Pulse Width Modulation, and this results in quite excessive GPIO switching IMHO. The current algorithm works by generating between 0 and 31 (or 32?) pulses of equal length for each of the LEDs depending on the desired brightness during each panel update. Each pulse means a column GPIO switch-on and a switch-off action, so for the 8 rows and 12 columns, for a desired 100 Hz panel update, the code tries to perform up to 8 x 12 x 2 x 32 (+ small change for row selection etc) =~ 6000 switches, or ca 600k (admittedly memory mapped) switches per second in total.
I made an alternative implementation that instead works like this:
for each panel update cycle:
update the target_brightness values
evolve the brightness vales for this update (ramp-up/ramp-down effect)
for each row do:
assign each LED of the row an integer brightness in 0..31
switch on all LEDs in that row with brightness > 0
sleep(max_pulse_width/32)
for step = 1 to 31 do
for LEDs in current row do:
if brightness == step: turn LED OFF
sleep(max_pulse_width/32)
// at this point all LEDs are turned off again
// and we can progress to the next row
Evidently each LED is switched on and off at most once per update cycle in this solution, so we need to perform only 12 x 8 x 2 x 100 =~ 20k GPIO switches per second for a 100 Hz update cycle.
I'll submit this to a new branch (sorry for the clutter), the patch will then hopefully fix these bugs:
"Why are the ILS equations now not symmetrical excepting the scaling factor?" https://tangentsoft.com/pidp8i/tktview?name=135d14a0c2
"Extremely low throttle values cause blinky display" https://tangentsoft.com/pidp8i/tktview?name=b8d9865f31
Perhaps also "ILS brightness takes seconds to settle at low throttle values" https://tangentsoft.com/pidp8i/tktview?name=26cc6b3614
In the initial test version I deliberately exaggerated the rise and decay delays just so that people testing this are convinced this is really still doing a "lamp" simulation. Perhaps one should make those settings user configurable anyway.
(27.1) By Heinz-Bernd Eggenstein (HBEggenstein) on 2021-11-11 08:49:31 edited from 27.0 in reply to 26 [link] [source]
Upps..... I goofed up here quite a bit :
Remember the 22Hz refresh rate I was seeing .. oh well that was caused by me being foolish and accidently bypassing the part in the install script that sets real-time capabilities to my test executables! My apologies for the confusion.
The good news is that the new ILS code that I wrote in response to this non-existing problem is now fast enough to run alongside the SIMH thread on a single core, at better than real PDP-8i speed, on a Raspi. I don't have a Pi Zero with soldered on header at the moment, but when I limit the number of cores to one in the boot commandline of my Pi3A+ and also limit the cpu clock to 800 MHz, I can still execute 5.script (after unthrottling of course) at 1.5 MIPS, the SIMH internal precalibration benchmark is ca 620k IPS, so there is even some headroom left.
Then again, now there is the Pi Zero 2 with 4 cores, so I'm not sure there is still demand for a single-core-capable solution.
If there is, I think the new ILS code has some merit, otherwise I guess it could just as well be reverted leaving only the actual bug fixes in that branch.
Sorry again for the confusion HB