Recently, have undergone some projects partly outside my usual coding stuff. In particular, got a manual mill (G0704) and then retrofitted it with a CNC kit (the FlashCut kit). It basically worked, as in the hardware went on easy enough and is basically functional. There was an issue with the Z axis pulley not staying on, as it basically screws on to the threads on the end and uses a set-screw, onto the threads. I ended up needing to ‘Loctite’ the threads (or, more accurately “apply the use of off-brand blue thread-locking compound, and ignore the use of a generalized trademark”).
Was having an issue with it, in that I couldn’t really get/keep the machine holding position while using the motor drivers internal to the 2.5A / 5A “Compact Microstepping Controller” box (which runs 3 fairly large steppers off a single 5 amp power brick; similar to a largish laptop-style power brick). Tried various options (using half-stepping rather than microstepping, this being at least a little more reliable, etc), and ended up making the rapid speeds and acceleration fairly slow.
Eventually got some external motor drivers (wired up to the box which luckily has a DB25 output for the motor signals), with a 48V 10A power supply, and the results are a little more impressive (as-in, now it doesn’t just randomly lose its position). Interesting, the motors run a lot cooler and quieter with the external drivers, despite better holding position and seemingly having more output torque (a bit of a mystery here). Ended up settling with running the motors at 2.84A/phase, which seemed like a good balance (between making the motors sufficiently powerful and not overheating stuff), though peak power could go outside the rated 10A for the supply (maybe should have got a bigger one).
Well, at least the software and the controller box’s output signals work reasonably reliably.
The kit cost a bit much though to be worth this much hassle. When a kit like this costs $k, you would hope it is better and more reliable and everything “just works”.
For this project, also went and did a more makeshift CNC conversion of a rotary table, using a NEMA17 motor, some custom made mounting hardware and pulleys, and a vacuum cleaner belt as the drive belt. Also used a knurling tool in the pulleys to try to minimize belt slip. Added another driver, and now the machine also has an ‘A’ axis.
Was also planning to do a retrofit to a lathe, but go a little more custom. Planning on replacing the main spindle motor with a big stepper (1800 oz-in NEMA34), and get a particularly large stepper driver for it (100V 8A per phase). This way I can use it both as a spindle and for C axis operations. I would use similar steppers for the X/Z axis to those on the mill (some 570 oz-in NEMA23 motors).
This project would differ some in that I am planning on going more custom on the CNC controller/software side. Current plan is to run some custom CNC software on a Raspberry Pi, with another controller generating the motor signals. While the RasPi could generate signals directly, the periodic interruptions due to Linux are enough to potentially result in lost steps with some of the faster moving motors (such as the spindle).
Did do a mock-up of some MSP430 code for doing motor controls, which basically works but limits motor speed some and has slow-ish (dial-up like) speeds for sending SPI commands due to the overall limited performance of the MSP430.
While I could technically get a faster microcontroller, another potentially interesting solution is to go full custom with an FPGA as well. In this case, some of the low-level motor control would be done directly in Verilog, with a customized microcontroller used for some of the software parts (the motor control part effectively would be done via an MMIO device). Ended up designing a new ISA for this.
BSR1 (or BtSR1) ISA
I decided to break with my prior SuperH based ISAs for this, while technically my B32V ISA variant would have also been fairly applicable.
BSR1 and B32V have a few points in common:
- Both use fixed-width 16-bit instructions.
- Though certain instruction pairs “may” be decoded as larger instructions.
- With some restrictions, both cases are treated as functionally equivalent.
- Though certain instruction pairs “may” be decoded as larger instructions.
- Both use 16x 32-bit GPRs (and a very similar C ABI).
- Both currently omit the use of a barrel shifter (TBD: may decide to keep shifter).
- Both include a limited form of the Load/Shift mechanism from my BJX1 ISA.
- Though the mechanism itself has many functional differences.
- Neither includes a hardware division operator.
- Things like integer division are emulated in software.
But, there are also points of difference:
- BSR1 is a new ISA, partly influenced by both SH and MSP430.
- BSR1 replaces (R0, Rn) memory access with (Rn, DLR).
- Drops @-Rn and @Rm+ addressing.
- Adds (PC, DLR) addressing.
- Also adds a (PC, DLR_i4) special case to help with code density.
- This reduces clashes with R0 being used as a GPR.
- Many operations with immediate fields are gone to free up more encoding space.
- Most immediate values have been moved to a DLR Load/Shift mechanism.
- Some cases still exist in the name of code density.
- Instruction encoding is different/incompatible.
- BSR1 is mostly OOnm, OOii, or OOnO
- B32V is mostly OnmO, OnOO, … with a SH-derived ISA.
- BSR1 will currently focus on smaller address spaces.
- My current prototype core design assumes the use of a 16-bit address space.
- Current design is focusing on a 32kB ROM space with 8kB of RAM.
- Technically, ISA can handle larger address spaces without too much issue.
The operation of the BSR1 ISA revolves a fair bit around the DLR register, which in the compiler is partially conflated with the SH MACL register. This register is also (like SH) used as a multiplier output, but (unlike SH) there is no MAC operation. DLR is generally treated as highly volatile, and takes over much of the role R0 served as a “stomping ground” register. As a result, R0 is freed up to be used as a GPR, and simplifies a lot of compiler logic which no longer has to have special cases depending on whether or not R0 is available. By extension, DLR is always assumed to be available, and in most cases it is undefined to either expect DLR to preserve a value nor whether the value in DLR will be a defined value following many operations (a processor could choose to treat several smaller operations as a single larger operation, and in doing so, need not necessarily update DLR in the expected way). Similarly, a processor can choose to execute the operations sequentially (as 16 bit instruction words) and still get the desired result.
So, operations may load an immediate or displacement value into DLR, and then an operation may use this value. Changing the exact sequence may allow a larger or smaller immediate or displacement to be encoded as needed.
For a small subset of the ISA, a signed 17-bit displacement may be represented in 32 bits:
- MOV.x Rm, (PC, disp17s)
- MOV.x (PC, disp17s), Rn
- BRA disp17s
- BSR disp17s
This allows accessing the whole 16-bit address space fairly easily, and extends reasonably cleanly to 25 or 33 bits (in a 48 or 64 bit instruction sequence). This is achieved via instructions which can load 13 bits into DLR, with the following opcode supplying an additional 4 bits. Many other instructions use DLR directly, which allows a 13-bit immediate to be encoded in an instruction pair (or 21 bits via a triple).
In some tests, code density seems to be roughly comparable to similar code compiled on the MSP430, which should hopefully allow getting a reasonable amount of code into a 32kB ROM or similar.
From the perspective of writing ASM code or generally working with the ISA, it seems to be a little less painful than SH (and B32V), which is sort of an improvement I guess. This is due mostly to the instruction set being a little more orthogonal and being able to gloss over a little more in the assembler/emitter stage.
Development is ongoing, and this is still at a fairly early stage, but results thus far seem to look promising.