eBike Motor-Assist Controller
A complete pedal-assist controller for an electric bike, built with two teammates for ECE 551 (Digital System Design and Synthesis) at UW–Madison in spring 2025. The design reads rider torque, pedal cadence, battery voltage, brake position, and hill incline; computes a target motor current from a configurable assist level; closes the loop with a hardware PID controller; and drives a brushless DC hub motor through six-step hall-sensor commutation with non-overlapping PWM gate signals. Measured values stream out over UART for telemetry.
The signal path: an ADC interface round-robins four channels over a 16-bit SPI master shared with the inertial sensor, whose gyro rate is integrated with accelerometer correction into a 13-bit incline. Sensor conditioning exponentially averages torque and motor current, measures cadence from a debounced pulse, and gates the PID error to zero when the battery is low or the rider stops pedaling. The desired-drive block computes target current as (torque minus a minimum) times cadence factor times incline factor times assist scale, through a pipelined multiplier.
The control and drive side: a PID running at 48 Hz through a decimator, with a saturating 18-bit integrator, a derivative taken against a three-sample-delayed error, and a 12-bit clamped output. The commutation block maps the three hall sensors to a six-step state, sets each coil to forward, reverse, or high-Z, and switches to regenerative braking when the brake is pulled. An 11-bit PWM feeds three non-overlap blocks that insert 32 clocks of dead time whenever a gate pair changes, so a high-side and low-side switch can never conduct at once. A push button cycles the assist level and shows it on two LEDs.
Synthesis with Synopsys Design Compiler to the SAED 32 nm LVT library at a 2.5 ns clock, with hierarchy flattened and hold fixed. The baseline flow came in at 13,406 µm² and 3,742 cells with setup and hold both met at zero slack. An area-driven experiment with compile_ultra -retime cut cell area 18% to 10,991 µm² and still met setup, but left one hold path 80 ps short even with hold fixing, so the baseline netlist was the one submitted.
Verification is in QuestaSim. Full-system testbenches close the loop through course-provided physics models: an ADC SPI slave, an IMU slave whose accelerometer follows the commanded yaw rate, and a hub-wheel model that turns the six gate-drive outputs into coil voltages, wheel speed, hall edges, and motor current. A FAST_SIM parameter shortens the 48 Hz decimator and the one-third-second cadence timeout to a simulation-friendly number of clocks. One bench sweeps torque, incline, battery, brake, and assist mode while the loop settles between steps; another checks that the PID error converges after every stimulus change and stops on failure; a third runs the same stimulus against the gate-level netlist. Unit benches cover the SPI master, ADC interface, PID against a plant model, cadence filter, incline saturation, sensor conditioning, and telemetry.
What we wrote versus what was provided: all of the RTL except the course-provided UART transmitter, cadence lookup table, and inertial integrator; every testbench and the shared testbench utilities; both synthesis scripts; and an IMU bring-up top for the DE0-Nano FPGA. The course supplied the physics models, the top-level port list, and the Quartus pin assignments.
The repository is private because it's a course project. Happy to walk through it or share access — email me.