Finished test bench implementation, fixed firmware bugs found during

simulation, document technical limits.
This commit is contained in:
Andrew Makousky 2020-09-10 04:43:38 -05:00
parent 6c1b2ba7d9
commit 2bce9ed244
5 changed files with 1905 additions and 48 deletions

View File

@ -1,3 +1,3 @@
Mac128kRTC.axf
MacPlusRTC.axf
avr_mcu_section.h
test/test-rtc

View File

@ -27,16 +27,6 @@
#include <avr/interrupt.h>
#include <avr/sleep.h>
#ifdef DO_SIMAVR
#include "avr_mcu_section.h"
AVR_MCU(32768, "attiny85");
AVR_MCU_SIMAVR_COMMAND (& GPIOR0 );
const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ =
{
{ AVR_MCU_VCD_SYMBOL("PORTB5"), .what = (void*)&PORTB, .mask = _BV(5) },
};
#endif
/********************************************************************/
// Simplified Arduino.h definitions.
typedef enum { false, true } bool; // Compatibility with C++.
@ -105,18 +95,22 @@ volatile boolean lastSerClock = 0;
volatile boolean serClockRising = false;
volatile boolean serClockFalling = false;
volatile enum SerialStateType serialState = SERIAL_DISABLED;
volatile byte serialState = SERIAL_DISABLED;
volatile byte serialBitNum = 0;
volatile byte address = 0;
volatile byte serialData = 0;
// Number of seconds since midnight, January 1, 1904. The serial
// register interface exposes this data as little endian. TODO
// VERIFY: Clock is initialized to January 1st, 1984? Or is this done
// by the ROM when the validity status is invalid?
/* Number of seconds since midnight, January 1, 1904. The serial
register interface exposes this data as little endian.
TODO VERIFY: Clock is initialized to January 1st, 1984? Or is this
done by the ROM when the validity status is invalid?
TODO INVESTIGATE: Does `simavr` not initialize non-zero variables?
Or is this a quirk with `avr-gcc`? */
volatile unsigned long seconds = 60UL * 60 * 24 * (365 * 4 + 1) * 20;
volatile byte pram[PRAM_SIZE] = {}; // PRAM initialized as zeroed data
volatile byte writeProtect = 0;
volatile byte pram[PRAM_SIZE] = {}; // PRAM initialized as zeroed data
#define shiftReadPB(output, bitNum, portBit) \
bitWrite(output, bitNum, ((PINB&_BV(portBit))) ? 1 : 0)
@ -128,7 +122,8 @@ volatile byte writeProtect = 0;
// Digital write in an open-drain fashion: set as output-low for zero,
// set as input-no-pullup for one.
void digitalWriteOD(uint8_t pin, uint8_t val) {
void digitalWriteOD(uint8_t pin, uint8_t val)
{
uint8_t bit = _BV(pin);
// cli();
if (val == 0) {
@ -141,22 +136,27 @@ void digitalWriteOD(uint8_t pin, uint8_t val) {
// sei();
}
void setup(void) {
void setup(void)
{
cli(); // Disable interrupts while we set things up
// TODO FIXME: Because `simavr` does not initialize non-zero global
// variables, we must repeat the initialization here.
seconds = 60UL * 60 * 24 * (365 * 4 + 1) * 20;
// OUTPUT: The 1Hz square wave (used for interrupts elsewhere in the system)
DDRB |= ONE_SEC_PIN;
DDRB |= (1<<ONE_SEC_PIN);
// INPUT: The processor pulls this pin low when it wants access
DDRB &= ~RTC_ENABLE_PIN;
PORTB &= ~RTC_ENABLE_PIN;
DDRB &= ~(1<<RTC_ENABLE_PIN);
PORTB &= ~(1<<RTC_ENABLE_PIN);
lastRTCEnable = PINB&(1<<RTC_ENABLE_PIN); // Initialize last value
// INPUT: The serial clock is driven by the processor
DDRB &= ~SERIAL_CLOCK_PIN;
PORTB &= ~SERIAL_CLOCK_PIN;
DDRB &= ~(1<<SERIAL_CLOCK_PIN);
PORTB &= ~(1<<SERIAL_CLOCK_PIN);
lastSerClock = PINB&(1<<SERIAL_CLOCK_PIN); // Initialize last value
// INPUT: We'll need to switch this to output when sending data
DDRB &= ~SERIAL_DATA_PIN;
PORTB &= ~SERIAL_DATA_PIN;
DDRB &= ~(1<<SERIAL_DATA_PIN);
PORTB &= ~(1<<SERIAL_DATA_PIN);
wdt_disable(); // Disable watchdog
bitSet(ACSR, ACD); // Disable Analog Comparator, don't need it, saves power
@ -176,19 +176,16 @@ void setup(void) {
TCNT0 = 0; // Clear the counter
bitClear(GTCCR, TSM); // Turns timers back on
#ifdef DO_SIMAVR
GPIOR0 = SIMAVR_CMD_VCD_START_TRACE;
#endif
sei(); //We're done setting up, enable those interrupts again
}
void clearState(void) {
void clearState(void)
{
// Return the pin to input mode
// cli();
DDRB &= ~SERIAL_DATA_PIN;
// PORTB &= ~SERIAL_DATA_PIN;
DDRB &= ~(1<<SERIAL_DATA_PIN);
// PORTB &= ~(1<<SERIAL_DATA_PIN);
// sei();
serialState = SERIAL_DISABLED;
serialBitNum = 0;
@ -200,7 +197,8 @@ void clearState(void) {
* An interrupt to both increment the seconds counter and generate the
* square wave
*/
void halfSecondInterrupt(void) {
void halfSecondInterrupt(void)
{
PINB = 1<<ONE_SEC_PIN; // Flip the one-second pin
if (!(PINB&(1<<ONE_SEC_PIN))) { // If the one-second pin is low
seconds++;
@ -215,7 +213,8 @@ void halfSecondInterrupt(void) {
* The actual serial communication can be done in the main loop, this
* way the clock still gets incremented.
*/
void handleRTCEnableInterrupt(void) {
void handleRTCEnableInterrupt(void)
{
boolean curRTCEnable = PINB&(1<<RTC_ENABLE_PIN);
if (lastRTCEnable && !curRTCEnable){ // Simulates a falling interrupt
serialState = RECEIVING_COMMAND;
@ -230,7 +229,8 @@ void handleRTCEnableInterrupt(void) {
* Same deal over here, the actual serial communication can be done in
* the main loop, this way the clock still gets incremented.
*/
void handleSerClockInterrupt(void) {
void handleSerClockInterrupt(void)
{
boolean curSerClock = PINB&(1<<SERIAL_CLOCK_PIN);
if (!lastSerClock && curSerClock) {
serClockRising = true;
@ -255,7 +255,8 @@ void handleSerClockInterrupt(void) {
* WRPROT_CMD: Special command: write-protect register.
* SUCCESS_ADDR: Successful address computation.
*/
uint8_t decodePramCmd(boolean writeRequest) {
uint8_t decodePramCmd(boolean writeRequest)
{
// Discard the first bit and the last two bits, it's not pertinent
// to address interpretation.
address = (address&~(1<<7))>>2;
@ -281,7 +282,8 @@ uint8_t decodePramCmd(boolean writeRequest) {
return SUCCESS_ADDR;
}
void loop(void) {
void loop(void)
{
if ((PINB&(1<<RTC_ENABLE_PIN))) {
clearState();
set_sleep_mode(0); // Sleep mode 0 == default, timers still running.
@ -369,8 +371,8 @@ void loop(void) {
// Write little endian clock data byte.
cli(); // Ensure that writes are atomic.
address = (address&0x03)<<3;
seconds &= ~(0xff<<address);
seconds |= serialData<<address;
seconds &= ~((unsigned long)0xff<<address);
seconds |= (unsigned long)serialData<<address;
sei();
}
break;
@ -424,6 +426,7 @@ void loop(void) {
// Read the data byte before continuing.
serialState = RECEIVING_XCMD_DATA;
serialBitNum = 0;
serialData = 0;
break;
}
@ -442,7 +445,8 @@ void loop(void) {
break;
// Write the PRAM register.
pram[address] = serialData;
if (!writeProtect)
pram[address] = serialData;
// Finished with the write command.
clearState();
break;
@ -469,17 +473,20 @@ void loop(void) {
/*
* Actually attach the interrupt functions
*/
ISR(PCINT0_vect) {
ISR(PCINT0_vect)
{
handleRTCEnableInterrupt();
handleSerClockInterrupt();
}
ISR(TIMER0_OVF_vect) {
ISR(TIMER0_OVF_vect)
{
halfSecondInterrupt();
}
// Arduino main function.
int main(void) {
int main(void)
{
setup();
for (;;) {

View File

@ -1,12 +1,10 @@
CFLAGS =
all: Mac128kRTC.axf MacPlusRTC.axf
Mac128kRTC.axf: MacRTC.c
avr-gcc $(CFLAGS) -o $@ -Os -mmcu=attiny85 -DNoXPRAM=1 $<
avr-gcc -o $@ -Os -mmcu=attiny85 -DNoXPRAM=1 $<
MacPlusRTC.axf: MacRTC.c
avr-gcc $(CFLAGS) -o $@ -Os -mmcu=attiny85 $<
avr-gcc -o $@ -Os -mmcu=attiny85 $<
clean:
rm -f Mac128kRTC.axf MacPlusRTC.axf

View File

@ -0,0 +1,12 @@
SIMAVR_PATH = $(HOME)/src/simavr
SIMAVR_INCLUDE = $(SIMAVR_PATH)/simavr/sim
SIMAVR_LIB_DIR = $(SIMAVR_PATH)/simavr/obj-arm-linux-gnueabihf
CFLAGS = -I $(HOME)/src/simavr/simavr/sim
all: test-rtc
test-rtc: test-rtc.c
gcc $(CFLAGS) -o $@ $< $(SIMAVR_LIB_DIR)/libsimavr.a -lelf -lrt
clean:
rm -f test-rtc

1840
firmware/rtc/test/test-rtc.c Normal file

File diff suppressed because it is too large Load Diff