Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: C12832_lcd USBDevice mbed-rtos mbed mmSPI_RTOS watchdog_RTOS
Fork of RTOS_project_fork_01 by
Diff: main.cpp
- Revision:
- 0:8e898e1270d6
- Child:
- 1:a3df81701818
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Tue Sep 17 19:42:49 2013 +0000
@@ -0,0 +1,918 @@
+/*----------------------------------------------//------------------------------
+ student : m-moore
+ email : gated.clock@gmail.com
+ class : embedded RTOS
+ directory : USB_device_project
+ file : main.cpp
+ date : september 19, 2013.
+----copyright-----------------------------------//------------------------------
+ licensed for personal and academic use.
+ commercial use of original code must be approved by the account-holder of
+ gated.clock@gmail.com
+----revision------------------------------------//------------------------------
+ this is the embedded RTOS class revision.
+ changes made since the USB device class release:
+
+ 1. a 'read all registers' feature has been added, which speeds up
+ CPU execution, since the UI obtains all of the register states after
+ each CPU clock. originally, each register was individually read.
+ now, all registers are read at once, if so requested by the python code.
+
+ 2. some 'if' statements were changed to 'switch' statements (neatening).
+
+ 3. added watchdog timers for the three threads. this via a meta-watchdog thread.
+
+ 4. added #defined-based option to either boot on error detection
+ (such as malloc fail) or use error(); function.
+
+ 5. the LCD is updated only if a display value is changed - reduced power
+ usage a little & reduces potential 'blinkieness'.
+
+ 6. BOOT notification on LCD.
+----description---------------------------------//------------------------------
+ overview:
+ program to provide round-trip communication between a python test-control
+ program running on a pc host, and a device-under-test CPU implemented on
+ an altera board. the pc-host communication is over USBSerial, and the
+ altera communication is over SPI.
+
+ features:
+ 1. multi-threaded design, use of memory-pools to transfer data between threads.
+ 2. use of USBDevice library for communication with PC host.
+ 3. use of mmSPI custom library for communication with FPGA.
+ 4. main thread provides USBSerial communication to/from host.
+ 5. SPI processing thread provides SPI communication to/from DUT.
+ 6. mmSPI library generates non-overlapping SPI and CPU clocks.
+ 7. background diagnostic thread provides LCD & error updates.
+ 8. meta watchdog thread monitors itself & the other threads.
+
+ indicators: (led<3:0> = LED<1:4>)
+ 1. LCD provides running counts for SPI and CPU clock cycles.
+ 2. led0 indicates main thread processing.
+ 3. led1 indicates SPI thread processing.
+ 4. led2 indicates LCD thread processing.
+ 5. led3 indicates watchdog thread processing.
+
+ implementation:
+ 1. main.processIncomingSerial(): accept incoming serial data from host,
+ and map it into tFromHost structures.
+ 2. SPIprocessingThread: take the incoming data structures instances, and
+ feed their content into mmSPI commands.
+ 3. mmSPI object: given commands/data passed from caller,
+ map them into SPI outgoing vectors and scan them into the FPGA.
+ 4. mmSPI object: receive incoming SPI vectors from FPGA.
+ make FPGA payload data available to caller.
+ 5. SPIprocessingThread: load tToHost structures with said FPGA payload data.
+ 6. main.processOutgoingSerial(): transfer tToHost structure data into a
+ serial outgoing buffer, and block-transfer it to the host PC.
+
+ electrical:
+ 1. four pins (and ground) attached to the zigbee header,
+ programmed as three SPI pins and the CPU clock.
+ 2. each of the four signals over twisted-pair.
+ 3. but some ribbon cable is used at the FPGA end.
+ 4. best if only the mbed application board USB cable is attached
+ to the host; if the mbed CPU board USB cable is also attached to
+ the host, then the python program may attempt to use the wrong USB
+ connection.
+ 5. no particular power sequence is needed for this system to work.
+
+ timing critical path: serial processing. the python code needs
+ a delay between serial access of 40mS conservatively.
+
+ testing:
+ the python UI provides the main testing mechanism.
+
+ USB connect.
+ 00. press 'CONNECT' button in UI. verify connection info in shell.
+
+ CPU register w/r
+ 01. type values into {R0,R1,R2,R3,PC,IR} UI entry-forms.
+ 02. press 'REG WRITE' UI button.
+ 03. press 'REG READ' UI button.
+ 04. verify that the read data is correct.
+
+ CPU main-memory w/r
+ 05. type an address into 'mmADR' UI entry-form.
+ 06. type data into 'mmVAL' UI entry-form.
+ 07. press 'MM WRITE' UI button.
+ 08. type a different address into 'mmADR' UI entry-form.
+ 09. type different data into 'mmVAL' UI entry-form.
+ 10. press 'MM WRITE' UI button.
+ 11. type address from (05) into 'mmADR' UI entry-form.
+ 12. press 'MM READ' UI button.
+ 13. verify that the data from (06) is seen in the 'mmVAL' entry form.
+ 14. type address from (08) into 'mmADR' UI entry-form.
+ 15. press 'MM READ' UI button.
+ 16. verify that the data from (09) is seen in the 'mmVAL' entry form.
+
+ CPU main-memory full load/dump.
+ 17. press 'PROGRAM' in the UI. select a program file in the dialog-popup.
+ 18. watch the load process in the shell text.
+ 19. press 'DUMP in the UI. select a main-memory dump file in the diaglog-popup.
+ 20. watch the dump process in the shell text.
+
+ CPU step function.
+ 21. press 'STEP' in the UI repeatedly, watch the UI display the
+ CPU register states as the current program is executed one CPU clock
+ at a time.
+
+ CPU run function.
+ 22. press 'RUN' in the UI. watch the current program run at high speed.
+ 23. press 'SLOW' in the UI. watch the current program run at slow speed.
+ 24. press 'STOP' in the UI. the program will stop execution.
+
+ CPU test function.
+ 25. press 'TEST' in the UI. the program will load,execute,dump,compare.
+ 26. tail -f testlog.txt to see test status.
+ 27. the test will repeat until 'STOP TEST' is pressed.
+ 28. long test performed by allowing this mode to continue.
+
+ UI exit function.
+ 29. press 'EXIT' in the UI. it will exit.
+-----includes-----------------------------------//----------------------------*/
+ #include "mbed.h" // general.
+ #include "USBSerial.h" // serial over USB.
+ #include "C12832_lcd.h" // LCD display.
+ #include "rtos.h" // RTOS.
+ #include "mmSPI.h" // SPI.
+ #include "watchdog.h" // watchdog.
+//---defines------------------------------------//------------------------------
+ #define LCD1 lcd.locate(0, 0); // LCD line 1.
+ #define LCD2 lcd.locate(0,11); // LCD line 2.
+ #define LCD3 lcd.locate(0,22); // LCD line 3.
+ #define LCD3 lcd.locate(0,22); // LCD line 3.
+ #define WATCHDOG_S 10 // watchdog timeout, in seconds.
+ #define ERROR_BOOT 1 // 1 means boot rather than error().
+ #define SPI_BYTES 8 // number of SPI bytes.
+ #define SPI_HZ 100000 // SPI frequency in Hz.
+ #define SER_BYTES 18 // serial in/out # of bytes.
+ #define SER_ALIGN 7 // '$' location in shift-register.
+ #define THREAD_0_WAIT 8 // multitasking wait mS.
+ #define THREAD_1_WAIT 2 // multitasking wait mS.
+ #define THREAD_2_WAIT 128 // multitasking wait mS.
+ #define THREAD_3_WAIT 128 // multitasking wait mS.
+ #define HB_MODULO 64 // heartbeat slowdown factor.
+ #define POOL_LEN 16 // memory pool dimension.
+ #define HCMD_SETREG 1 // host command 'set register'.
+ #define HCMD_GETREG 2 // host command 'get register'.
+ #define HCMD_SETMM 3 // host command 'set main-memory.'
+ #define HCMD_GETMM 4 // host command 'get main-memory.'
+ #define HCMD_STEP 5 // host command 'step-CPU'.
+ #define HCMD_SETIR 6 // host command 'set-IR'.
+ #define HCMD_GETALLREG 7 // host command 'get-all-registers'.
+ #define CPU_REG_0 0 // CPU register 0.
+ #define CPU_REG_1 1 // CPU register 1.
+ #define CPU_REG_2 2 // CPU register 2.
+ #define CPU_REG_3 3 // CPU register 3.
+ #define CPU_REG_PC 4 // CPU Program Counter.
+ #define CPU_IR_H 5 // CPU IR high-byte.
+ #define CPU_IR_L 6 // CPU IR low-byte.
+//--externals-----------------------------------//------------------------------
+ extern "C" void mbed_reset(); // processor reset.
+//--global_definitions--------------------------//------------------------------
+ struct tFromHost // command from host.
+ {
+ char cCommand; // command from host.
+ char cRegisterID; // which CPU register.
+ char cRegisterValue; // write this to CPU register.
+ char cIRValueH; // write this to IR.
+ char cIRValueL; // write this to IR.
+ char cMMaddress; // access this MM address.
+ char cMMdataH; // MM content high byte.
+ char cMMdataL; // MM content low byte.
+ };
+ MemoryPool<tFromHost, POOL_LEN> mPoolFromHost;
+ Queue <tFromHost, POOL_LEN> qFromHost;
+
+//----
+
+ struct tToHost // reply to host.
+ {
+ char cCommand; // command executed.
+ char cRegisterID; // which CPU register read.
+ char cRegisterValue; // data from CPU register.
+ char cMMaddress; // which MM address read.
+ char cMMdataH; // MM content high byte.
+ char cMMdataL; // MM content low byte.
+ char cReg0; // data from R0.
+ char cReg1; // data from R1.
+ char cReg2; // data from R2.
+ char cReg3; // data from R3.
+ char cPC; // data from program counter.
+ char cIRH; // high byte from instruction register.
+ char cIRL; // low byte from instruction register.
+ };
+ MemoryPool<tToHost, POOL_LEN> mPoolToHost;
+ Queue <tToHost, POOL_LEN> qToHost;
+
+ Queue<int, POOL_LEN> queueWatchdogThread_0; // main thread watchdog notice.
+ Queue<int, POOL_LEN> queueWatchdogThread_1; // thread 1 watchdog notice.
+ Queue<int, POOL_LEN> queueWatchdogThread_2; // thread 2 watchdog notice.
+//--global_variables----------------------------//------------------------------
+ char gpcSerialFromHost[SER_BYTES]; // incoming serial buffer.
+ char gpcSerialToHost [SER_BYTES]; // outgoing serial buffer.
+ char gcNewCommand; // new command from host.
+ int gdRoundTrip; // +1 from host, -1 to host.
+ tToHost * gpToHost; // to-host structure.
+ osEvent gqToHostEvent; // incoming message event.
+ unsigned long gulSPIclkCount; // SPI clock count.
+ unsigned long gulCPUclkCount; // CPU clock count.
+//--global_instances----------------------------//------------------------------
+ USBSerial serial; // serial over usb.
+ C12832_LCD lcd; // LCD display.
+ DigitalOut led0(LED4); // thread heartbeat.
+ DigitalOut led1(LED3); // thread heartbeat.
+ DigitalOut led2(LED2); // thread heartbeat.
+ DigitalOut led3(LED1); // SPI reply underflow warning.
+//-------prototypes-----------------------------//------------------------------
+ int main(); // main.
+ void processIncomingSerial(); // process incoming host data.
+ void processOutgoingSerial(); // process outgoing data to host.
+ void SPIprocessingThread(void const *args); // SPI-side processing.
+ void diagnosticThread (void const *args); // LCD and LED notifications.
+ void watchdogThread (void const *args); // overall watchdog.
+ char ascii_nibble_to_binary(char cAscii); // ascii nibble -> binary.
+ char binary_to_ascii_nibble(char cBinary); // binary -> ascii nibble.
+ void clear_tFromHost(tFromHost *ptFromHost);// initialize structure.
+ void clear_tToHost (tToHost *ptToHost); // initialize structure.
+//==============================================//==============================
+ int main(void) // USBSerial processing thread.
+ {
+ int dHeartbeat; // heartbeat counter.
+ int dLoop; // loop index.
+
+ gpToHost = NULL; // initialize global.
+ gcNewCommand = 0; // initialize global.
+ gdRoundTrip = 1024; // initialize global.
+ gulSPIclkCount = 0; // initialize global.
+ gulCPUclkCount = 0; // initialize global.
+ led0 = 0; // initialize global.
+ led1 = 0; // initialize global.
+ led2 = 0; // initialize global.
+ led3 = 0; // initialize global.
+ dHeartbeat = 0; // initialize local.
+ dLoop = 0; // initialize local.
+
+ // BOOT notification.
+ lcd.cls(); LCD2; lcd.printf(" BOOT"); wait(1.0);
+
+ // initialize serial-in shift-register.
+ for (dLoop = 0; dLoop < SER_BYTES; dLoop++) gpcSerialFromHost[dLoop] = 0;
+
+ // thread-out SPI-side processing.
+ Thread thread_1(SPIprocessingThread,NULL,osPriorityHigh,DEFAULT_STACK_SIZE,NULL);
+
+ // thread-out diagnostics.
+ Thread thread_2(diagnosticThread, NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
+
+ // thread-out universal watchdog.
+ Thread thread_3(watchdogThread, NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
+
+ while(1) // main loop.
+ {
+ processIncomingSerial(); // process data in from host.
+ processOutgoingSerial(); // process data out to host.
+
+ dHeartbeat++; // thread heartbeat.
+ if (!(dHeartbeat % HB_MODULO)) led0 = !led0;
+ queueWatchdogThread_0.put((int *) 0,1); // adds 1mS to wait.
+ Thread::wait(THREAD_0_WAIT - 1); // multitasking.
+ } // main loop.
+ } // main.
+/*----------------------------------------------//----------------------------*/
+/*
+ the python program running on the host is sending/receiving ascii characters
+ which represent command/data binary nibbles. the python program will send
+ the '$' character for command-string alignment. this function reads-in the
+ incoming serial stream when any serial data is available, into a shift-register,
+ and breaks upon detection of the '$' alignment character for python
+ command-processing. at that point the shift-register will look something like
+ [0] [1] [2] [3] [4] [5] [6] [7]
+ '1' '2' '3' '4' x x x '$' (means write 0x34 to CPU R2).
+
+
+ command-host register-number interpretation:
+ 0 = CPU R0.
+ 1 = CPU R1.
+ 2 = CPU R2.
+ 3 = CPU R3.
+ 4 = CPU program-counter.
+ 5 = CPU instruction-register high-byte.
+ 6 = CPU instruction-register low-byte.
+
+ instruction-register write is specially implemented,
+ instruction-register read is implemented as two standard register-reads.
+
+ host-command shift-register interpretation:
+
+ gpcSerialFromHost[0] = command.
+ subsequent interpretation depends on the command.
+
+----
+ if command = HCMD_SETREG (write-CPU-register) or HCMD_GETREG (read-CPU-register):
+
+ gpcSerialFromHost[1] = register number (see above).
+ gpcSerialFromHost[2] = register content, high nibble.
+ gpcSerialFromHost[3] = register content, low nibble.
+ gpcSerialFromHost[4] = not used.
+ gpcSerialFromHost[5] = not used.
+ gpcSerialFromHost[6] = not used.
+
+----
+ if command = HCMD_SETIR (write-CPU-instruction-register):
+
+ gpcSerialFromHost[1] = IR register number, implied anyway.
+ gpcSerialFromHost[2] = IR write value high byte high nibble.
+ gpcSerialFromHost[3] = IR write value high byte low nibble.
+ gpcSerialFromHost[4] = IR write value low byte high nibble.
+ gpcSerialFromHost[5] = IR write value low byte low nibble.
+ gpcSerialFromHost[6] = not used.
+
+----
+ if command = HCMD_SETMM (write to CPU main-memory) or HCMD_GETMM (read from CPU main-memory):
+
+ gpcSerialFromHost[1] = MM address high nibble.
+ gpcSerialFromHost[2] = MM address low nibble.
+ gpcSerialFromHost[3] = MM content high byte high nibble.
+ gpcSerialFromHost[4] = MM content high byte low nibble.
+ gpcSerialFromHost[5] = MM content low byte high nibble.
+ gpcSerialFromHost[6] = MM content low byte low nibble.
+
+ the above also applies to function 'processOutgoingSerial'.
+*/
+
+ void processIncomingSerial(void) // process incoming serial data.
+ {
+ int dLoop; // loop index.
+ tFromHost * pFromHost; // from-host structure.
+
+ while(serial.available()) // while data from host is available.
+ {
+ // shift-in the serial stream.
+ for (dLoop = 0; dLoop < (SER_BYTES - 1); dLoop++)
+ gpcSerialFromHost[dLoop] = gpcSerialFromHost[dLoop + 1];
+ gpcSerialFromHost[SER_BYTES - 1] = serial._getc();
+
+ if (gpcSerialFromHost[SER_ALIGN] == '$')// data from host is aligned.
+ {
+ gcNewCommand = 1; // new host command just recognised.
+ break; // need to process aligned data.
+ } // data from host is aligned.
+ } // while data from host is available.
+
+ // even if more data awaits from the
+ // incoming serial stream, we now need
+ // to process the aligned data recognised
+ // as a command from the host.
+
+ if (gcNewCommand) // execute once per new command.
+ {
+ pFromHost = mPoolFromHost.alloc(); // allocate next pool entry.
+ if (!pFromHost) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r processIncomingSerial : FATAL malloc error for pFromHost. \n\r");
+ }
+
+ clear_tFromHost(pFromHost); // initialize structure.
+
+ // copy-in host message.
+ pFromHost->cCommand = ascii_nibble_to_binary(gpcSerialFromHost[0]);
+
+//----
+
+ switch(pFromHost->cCommand) // command dependency.
+ {
+ case HCMD_SETREG : // host command 'set register'.
+ {
+ pFromHost->cRegisterID = ascii_nibble_to_binary(gpcSerialFromHost[1]);
+ pFromHost->cRegisterValue = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[3]);
+ break;
+ } // host command 'set register'.
+
+ case HCMD_GETREG : // host command 'get register'.
+ {
+ pFromHost->cRegisterID = ascii_nibble_to_binary(gpcSerialFromHost[1]);
+ pFromHost->cRegisterValue = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[3]);
+ gdRoundTrip++; // expected reply to host is pending.
+ break;
+ } // host command 'get register'.
+
+ case HCMD_SETMM : // host command 'set main-memory.'
+ {
+ pFromHost->cMMaddress = ((ascii_nibble_to_binary(gpcSerialFromHost[1])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[2]);
+ pFromHost->cMMdataH = ((ascii_nibble_to_binary(gpcSerialFromHost[3])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[4]);
+ pFromHost->cMMdataL = ((ascii_nibble_to_binary(gpcSerialFromHost[5])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[6]);
+ break;
+ } // host command 'set main-memory.'
+
+ case HCMD_GETMM : // host command 'get main-memory.'
+ {
+ pFromHost->cMMaddress = ((ascii_nibble_to_binary(gpcSerialFromHost[1])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[2]);
+ pFromHost->cMMdataH = ((ascii_nibble_to_binary(gpcSerialFromHost[3])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[4]);
+ pFromHost->cMMdataL = ((ascii_nibble_to_binary(gpcSerialFromHost[5])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[6]);
+
+ gdRoundTrip++; // expected reply to host is pending.
+ break;
+ } // host command 'get main-memory.'
+
+ case HCMD_STEP : // host command 'step-CPU'.
+ {
+ break;
+ } // host command 'step-CPU'.
+
+ case HCMD_SETIR : // host command 'set-IR'.
+ {
+ pFromHost->cIRValueH = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[3]);
+ pFromHost->cIRValueL = ((ascii_nibble_to_binary(gpcSerialFromHost[4])) << 4) +
+ ascii_nibble_to_binary(gpcSerialFromHost[5]);
+ break;
+ } // host command 'set-IR'.
+
+ case HCMD_GETALLREG : // host command 'get-all-registers'.
+ {
+ gdRoundTrip++; // expected reply to host is pending.
+ break;
+ } // host command 'get-all-registers'.
+
+ default : // default.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r processIncomingSerial : ERROR unrecognised command from host. \n\r");
+ break;
+ } // default.
+ } // command dependency.
+
+//----
+
+ qFromHost.put(pFromHost); // send out for processing.
+ gcNewCommand = 0; // don't execute until next new command.
+ } // execute once per new command.
+ Thread::wait(THREAD_0_WAIT); // multitasking.
+ } // processIncomingSerial
+/*----------------------------------------------//----------------------------*/
+ void processOutgoingSerial(void) // prepare/transmit data to host.
+ {
+ int dLoop; // loop index.
+
+ gqToHostEvent = qToHost.get(1); // check for reply back to host.
+
+ // if new reply to host:
+ if (gqToHostEvent.status == osEventMessage)
+ {
+ // bring it in from the queue.
+ gpToHost = (tToHost *) gqToHostEvent.value.p;
+ if (!gpToHost) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r processOutgoingSerial : FATAL NULL gpToHost pointer. \n\r");
+ }
+
+ // clear outgoing buffer.
+ for (dLoop = 0; dLoop < SER_BYTES; dLoop++) gpcSerialToHost[dLoop] = 0;
+
+ switch(gpToHost->cCommand) // the from-host command was looped to here.
+ {
+ case HCMD_SETREG : // host command 'set register'.
+ {
+ break;
+ } // host command 'set register'.
+
+ case HCMD_GETREG : // host command 'get register'.
+ {
+ gpcSerialToHost[0] = binary_to_ascii_nibble( gpToHost->cCommand);
+ gpcSerialToHost[1] = binary_to_ascii_nibble( gpToHost->cRegisterID);
+ gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 4) & 0x0F);
+ gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 0) & 0x0F);
+ gpcSerialToHost[4] = '\n'; // signals end of transfer.
+
+ // transmit to the host.
+ serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);
+ gdRoundTrip--; // expected reply sent to host.
+ break;
+ } // host command 'get register'.
+
+ case HCMD_SETMM : // host command 'set main-memory.'
+ {
+ break;
+ } // host command 'set main-memory.'
+
+ case HCMD_GETMM : // host command 'get main-memory.'
+ {
+ gpcSerialToHost[0] = binary_to_ascii_nibble( gpToHost->cCommand);
+ gpcSerialToHost[1] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 4) & 0x0F);
+ gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 0) & 0x0F);
+ gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cMMdataH ) >> 4) & 0x0F);
+ gpcSerialToHost[4] = binary_to_ascii_nibble(((gpToHost->cMMdataH ) >> 0) & 0x0F);
+ gpcSerialToHost[5] = binary_to_ascii_nibble(((gpToHost->cMMdataL ) >> 4) & 0x0F);
+ gpcSerialToHost[6] = binary_to_ascii_nibble(((gpToHost->cMMdataL ) >> 0) & 0x0F);
+ gpcSerialToHost[7] = '\n'; // signals end of transfer.
+
+ // transmit to the host.
+ serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);
+ gdRoundTrip--; // expected reply sent to host.
+ break;
+ } // host command 'get main-memory.'
+
+ case HCMD_STEP : // host command 'step-CPU'.
+ {
+ break;
+ } // host command 'step-CPU'.
+
+ case HCMD_SETIR : // host command 'set-IR'.
+ {
+ break;
+ } // host command 'set-IR'.
+
+ case HCMD_GETALLREG : // host command 'get-all-registers'.
+ {
+ gpcSerialToHost[ 0] = binary_to_ascii_nibble( gpToHost->cCommand);
+ gpcSerialToHost[ 1] = binary_to_ascii_nibble(((gpToHost->cReg0) >> 4) & 0x0F);
+ gpcSerialToHost[ 2] = binary_to_ascii_nibble(((gpToHost->cReg0) >> 0) & 0x0F);
+ gpcSerialToHost[ 3] = binary_to_ascii_nibble(((gpToHost->cReg1) >> 4) & 0x0F);
+ gpcSerialToHost[ 4] = binary_to_ascii_nibble(((gpToHost->cReg1) >> 0) & 0x0F);
+ gpcSerialToHost[ 5] = binary_to_ascii_nibble(((gpToHost->cReg2) >> 4) & 0x0F);
+ gpcSerialToHost[ 6] = binary_to_ascii_nibble(((gpToHost->cReg2) >> 0) & 0x0F);
+ gpcSerialToHost[ 7] = binary_to_ascii_nibble(((gpToHost->cReg3) >> 4) & 0x0F);
+ gpcSerialToHost[ 8] = binary_to_ascii_nibble(((gpToHost->cReg3) >> 0) & 0x0F);
+ gpcSerialToHost[ 9] = binary_to_ascii_nibble(((gpToHost->cPC) >> 4) & 0x0F);
+ gpcSerialToHost[10] = binary_to_ascii_nibble(((gpToHost->cPC) >> 0) & 0x0F);
+ gpcSerialToHost[11] = binary_to_ascii_nibble(((gpToHost->cIRH) >> 4) & 0x0F);
+ gpcSerialToHost[12] = binary_to_ascii_nibble(((gpToHost->cIRH) >> 0) & 0x0F);
+ gpcSerialToHost[13] = binary_to_ascii_nibble(((gpToHost->cIRL) >> 4) & 0x0F);
+ gpcSerialToHost[14] = binary_to_ascii_nibble(((gpToHost->cIRL) >> 0) & 0x0F);
+ gpcSerialToHost[15] = '\n'; // signals end of transfer.
+
+ // transmit to the host.
+ serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);
+ gdRoundTrip--; // expected reply sent to host.
+ break;
+ } // host command 'get-all-registers'.
+
+ default :
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r processOutgoingSerial : ERROR unrecognised command from host. \n\r");
+ break;
+ } // default.
+ } // switch(gpToHost->cCommand)
+
+ mPoolToHost.free(gpToHost); // done with this queue entry.
+ gpToHost = NULL; // clear pointer.
+ } // if new reply to host.
+ } // processOutgoingSerial.
+/*----------------------------------------------//----------------------------*/
+// the pcSendBuffer and pcReceiveBuffer arrays are not used by this function,
+// but they are declared by this function, and their pointers are passed
+// down to the mmSPI library for its use of them.
+// note- the prefix 'pc' means 'pointer of type character', not 'personal computer'.
+
+ void SPIprocessingThread(void const *args) // process host-commands via SPI.
+ {
+ char pcSendBuffer [SPI_BYTES]; // SPI send buffer.
+ char pcReceiveBuffer[SPI_BYTES]; // SPI receive buffer.
+ char pcRegisters [SPI_BYTES]; // fetch-all-registers vector.
+ int dHeartbeat; // heartbeat counter.
+ int dMemoryRead; // read main-memory into this.
+ int dLoop; // loop index.
+ osEvent qFromHostEvent; // incoming message event.
+ tFromHost * pFromHost; // message structure.
+ tToHost * gpToHost; // message structure.
+ mmSPI * pSPI; // SPI.
+
+ pFromHost = NULL; // NULL pointers.
+ gpToHost = NULL;
+ pSPI = NULL;
+ dHeartbeat = 0; // clear variables.
+ dMemoryRead = 0;
+ dLoop = 0;
+ // clear SPI vectors.
+ for (dLoop = 0; dLoop < SPI_BYTES; dLoop++) pcSendBuffer [dLoop] = 0;
+ for (dLoop = 0; dLoop < SPI_BYTES; dLoop++) pcReceiveBuffer[dLoop] = 0;
+
+ pSPI = new mmSPI; // SPI allocation.
+ if (!pSPI) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r SPIprocessingThread : FATAL new error for pSPI. \n\r");
+ }
+
+ pSPI->setSendBuffer (pcSendBuffer); // set SPI send buffer.
+ pSPI->setReceiveBuffer(pcReceiveBuffer); // set SPI receive buffer.
+ pSPI->setNumberOfBytes(SPI_BYTES); // set SPI number of bytes.
+ pSPI->setSPIfrequency (SPI_HZ); // set SPI clock frequency.
+
+ while(1) // thread loop.
+ {
+ qFromHostEvent = qFromHost.get(1); // check for incoming host command.
+
+ // if new host command:
+ if (qFromHostEvent.status == osEventMessage)
+ {
+ // bring it in from the queue.
+ pFromHost = (tFromHost *) qFromHostEvent.value.p;
+ if (!pFromHost) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r SPIprocessingThread : FATAL NULL pFromHost pointer. \n\r");
+ }
+
+ switch(pFromHost->cCommand) // host command decode.
+ {
+ case HCMD_SETREG : // set CPU register.
+ {
+ switch(pFromHost->cRegisterID) // which register to write to.
+ {
+ case CPU_REG_0 : {pSPI->write_register(CPU_REG_0 , pFromHost->cRegisterValue); break;}
+ case CPU_REG_1 : {pSPI->write_register(CPU_REG_1 , pFromHost->cRegisterValue); break;}
+ case CPU_REG_2 : {pSPI->write_register(CPU_REG_2 , pFromHost->cRegisterValue); break;}
+ case CPU_REG_3 : {pSPI->write_register(CPU_REG_3 , pFromHost->cRegisterValue); break;}
+ case CPU_REG_PC : {pSPI->write_register(CPU_REG_PC, pFromHost->cRegisterValue); break;}
+ default : {break;}
+ } // which register to write to.
+ break;
+ } // set CPU register.
+
+ case HCMD_SETIR: // set instruction register.
+ {
+ pSPI->write_IR(pFromHost->cIRValueH, pFromHost->cIRValueL);
+ break;
+ } // set instruction register.
+
+ case HCMD_GETREG : // get CPU register.
+ {
+ gpToHost = mPoolToHost.alloc(); // allocate next pool entry.
+ if (!gpToHost) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r");
+ }
+
+ clear_tToHost(gpToHost); // initialize structure.
+
+ switch(pFromHost->cRegisterID) // which register to read from.
+ {
+ case CPU_REG_0 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_0 ); break;}
+ case CPU_REG_1 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_1 ); break;}
+ case CPU_REG_2 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_2 ); break;}
+ case CPU_REG_3 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_3 ); break;}
+ case CPU_REG_PC : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_PC); break;}
+ case CPU_IR_H : {gpToHost->cRegisterValue = pSPI->read_register(CPU_IR_H ); break;}
+ case CPU_IR_L : {gpToHost->cRegisterValue = pSPI->read_register(CPU_IR_L ); break;}
+ default : {break;}
+ } // which register to read from.
+
+ // loop-back to host.
+ gpToHost->cCommand = pFromHost->cCommand;
+ gpToHost->cRegisterID = pFromHost->cRegisterID;
+
+ qToHost.put(gpToHost); // send up for processing.
+ break;
+ } // get CPU register.
+
+ case HCMD_GETALLREG : // get all CPU registers.
+ {
+ gpToHost = mPoolToHost.alloc(); // allocate next pool entry.
+ if (!gpToHost) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r");
+ }
+
+ clear_tToHost(gpToHost); // initialize structure.
+
+ // tell SPI to return all reg content.
+ pSPI->read_all_registers(pcRegisters);
+
+ gpToHost->cReg0 = pcRegisters[6]; // transfer to outgoing structure.
+ gpToHost->cReg1 = pcRegisters[5];
+ gpToHost->cReg2 = pcRegisters[4];
+ gpToHost->cReg3 = pcRegisters[3];
+ gpToHost->cPC = pcRegisters[2];
+ gpToHost->cIRH = pcRegisters[1];
+ gpToHost->cIRL = pcRegisters[0];
+
+ // loop-back to host.
+ gpToHost->cCommand = pFromHost->cCommand;
+ qToHost.put(gpToHost); // send up for processing.
+
+ break;
+ } // get all CPU registers.
+
+ case HCMD_SETMM : // do main-memory write.
+ {
+ pSPI->write_memory(pFromHost->cMMdataH, pFromHost->cMMdataL, pFromHost->cMMaddress);
+ break;
+ } // do main-memory write.
+
+ case HCMD_GETMM : // do main-memory read.
+ {
+ gpToHost = mPoolToHost.alloc(); // allocate next pool entry.
+ if (!gpToHost) // failure detection.
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r");
+ }
+
+ clear_tToHost(gpToHost); // initialize structure.
+
+ // read from CPU memory.
+ dMemoryRead = pSPI->read_memory(pFromHost->cMMaddress);
+ gpToHost->cMMdataH = (dMemoryRead >> 8) & 0xFF;
+ gpToHost->cMMdataL = (dMemoryRead >> 0) & 0xFF;
+
+ // loop-back to host.
+ gpToHost->cCommand = pFromHost->cCommand;
+ gpToHost->cMMaddress = pFromHost->cMMaddress;
+
+ qToHost.put(gpToHost); // send up for processing.
+ break;
+ } // do main-memory read.
+
+ case HCMD_STEP : // step the CPU.
+ {
+ pSPI->step();
+ break;
+ } // step the CPU.
+ default : break;
+ } // host command decode.
+ mPoolFromHost.free(pFromHost); // done with this queue entry.
+ pFromHost = NULL; // clear pointer.
+ } // if new host command.
+
+ gulSPIclkCount = pSPI->SPIClockCount(); // propagate to global variable.
+ gulCPUclkCount = pSPI->CPUClockCount(); // propagate to global variable.
+
+ // thread heartbeat.
+ dHeartbeat++; if (!(dHeartbeat % HB_MODULO)) led1 = !led1;
+ queueWatchdogThread_1.put((int *) 0,1); // adds 1mS to wait.
+ Thread::wait(THREAD_1_WAIT - 1); // cooperative multitasking.
+ } // thread loop.
+ } // SPIprocessingThread.
+/*----------------------------------------------//----------------------------*/
+ void diagnosticThread(void const *args) // LCD notification & error check.
+ {
+ // track previous values.
+ static unsigned long ulLastSPIclkCount = 1;
+ static unsigned long ulLastCPUclkCount = 1;
+ int dHeartbeat; // heartbeat counter.
+
+ dHeartbeat = 0; // initialize.
+
+ while(1) // thread loop.
+ {
+ // if message round trip
+ // count not consistent.
+ if (gdRoundTrip > 1025 || gdRoundTrip < 1024)
+ {
+ if (ERROR_BOOT) mbed_reset(); else
+ error("\n\r diagnosticThread : ERROR serial reply underflow. \n\r");
+ }
+
+ // update LCD only if a display
+ // value has changed. anti-blink,
+ // save a bit of power.
+ if (gulSPIclkCount != ulLastSPIclkCount ||
+ gulCPUclkCount != ulLastCPUclkCount )
+ {
+ lcd.cls(); // clear LCD display.
+ LCD1; // lcd line 1.
+ lcd.printf(" RTOS CLASS PROJECT");
+
+ LCD2; // lcd line 2.
+ lcd.printf(" %11lu = SPI clocks",gulSPIclkCount);
+
+ LCD3; // lcd line 3.
+ lcd.printf(" %11lu = CPU clocks",gulCPUclkCount);
+ }
+
+ ulLastSPIclkCount = gulSPIclkCount; // pipeline.
+ ulLastCPUclkCount = gulCPUclkCount; // pipeline.
+
+ dHeartbeat++; // thread heartbeat.
+ if (!(dHeartbeat % HB_MODULO)) led2 = !led2;
+ queueWatchdogThread_2.put((int *) 0,1); // adds 1mS to wait.
+ Thread::wait(THREAD_2_WAIT - 1); // multitasking.
+ } // thread loop.
+ } // diagnosticThread.
+/*----------------------------------------------//----------------------------*/
+ // this is its own watchdog.
+
+ void watchdogThread(void const *args) // overall watchdog.
+ {
+ int dHeartbeat; // heartbeat counter.
+ osEvent queueEvent; // queue event
+ Watchdog watchdog; // the one and only watchdog.
+
+ dHeartbeat = 0; // initialize.
+
+ watchdog.kick(WATCHDOG_S); // initialize watchdog.
+
+ while (1) // thread loop.
+ {
+ // all other threads report-in.
+ // blocking wait on all of them.
+ queueEvent = queueWatchdogThread_0.get(osWaitForever);
+ queueEvent = queueWatchdogThread_1.get(osWaitForever);
+ queueEvent = queueWatchdogThread_2.get(osWaitForever);
+
+ watchdog.kick(); // reset watchdog timer.
+
+ dHeartbeat++; // thread heartbeat.
+ if (!(dHeartbeat % HB_MODULO)) led3 = !led3;
+ Thread::wait(THREAD_3_WAIT); // multitasking.
+ } // thread loop.
+ }
+/*----------------------------------------------//----------------------------*/
+ char ascii_nibble_to_binary(char cAscii) // ascii nibble -> binary.
+ {
+ char cBinary; // converted value.
+
+ switch(cAscii)
+ {
+ case 'F' : {cBinary = 15; break;}
+ case 'E' : {cBinary = 14; break;}
+ case 'D' : {cBinary = 13; break;}
+ case 'C' : {cBinary = 12; break;}
+ case 'B' : {cBinary = 11; break;}
+ case 'A' : {cBinary = 10; break;}
+ case 'f' : {cBinary = 15; break;}
+ case 'e' : {cBinary = 14; break;}
+ case 'd' : {cBinary = 13; break;}
+ case 'c' : {cBinary = 12; break;}
+ case 'b' : {cBinary = 11; break;}
+ case 'a' : {cBinary = 10; break;}
+ case '9' : {cBinary = 9; break;}
+ case '8' : {cBinary = 8; break;}
+ case '7' : {cBinary = 7; break;}
+ case '6' : {cBinary = 6; break;}
+ case '5' : {cBinary = 5; break;}
+ case '4' : {cBinary = 4; break;}
+ case '3' : {cBinary = 3; break;}
+ case '2' : {cBinary = 2; break;}
+ case '1' : {cBinary = 1; break;}
+ case '0' : {cBinary = 0; break;}
+ default : {cBinary = 0; break;}
+ } // switch(cAscii).
+ return(cBinary); // return the binary.
+ } // ascii_nibble_to_binary.
+/*----------------------------------------------//----------------------------*/
+ char binary_to_ascii_nibble(char cBinary) // binary -> ascii nibble.
+ {
+ char cAscii; // converted value.
+
+ switch(cBinary)
+ {
+ case 15 : {cAscii = 'F'; break;}
+ case 14 : {cAscii = 'E'; break;}
+ case 13 : {cAscii = 'D'; break;}
+ case 12 : {cAscii = 'C'; break;}
+ case 11 : {cAscii = 'B'; break;}
+ case 10 : {cAscii = 'A'; break;}
+ case 9 : {cAscii = '9'; break;}
+ case 8 : {cAscii = '8'; break;}
+ case 7 : {cAscii = '7'; break;}
+ case 6 : {cAscii = '6'; break;}
+ case 5 : {cAscii = '5'; break;}
+ case 4 : {cAscii = '4'; break;}
+ case 3 : {cAscii = '3'; break;}
+ case 2 : {cAscii = '2'; break;}
+ case 1 : {cAscii = '1'; break;}
+ case 0 : {cAscii = '0'; break;}
+ default : {cAscii = '0'; break;}
+ } // switch(cBinary).
+ return(cAscii); // return the binary.
+ } // binary_to_ascii_nibble.
+/*----------------------------------------------//----------------------------*/
+ void clear_tFromHost(tFromHost * ptFromHost)// clear structure.
+ {
+ ptFromHost->cCommand = 0x00;
+ ptFromHost->cRegisterID = 0x00;
+ ptFromHost->cRegisterValue = 0x00;
+ ptFromHost->cIRValueH = 0x00;
+ ptFromHost->cIRValueL = 0x00;
+ ptFromHost->cMMaddress = 0x00;
+ ptFromHost->cMMdataH = 0x00;
+ ptFromHost->cMMdataL = 0x00;
+ } // clear_tFromHost.
+/*----------------------------------------------//----------------------------*/
+ void clear_tToHost(tToHost * ptToHost) // clear structure.
+ {
+ ptToHost->cCommand = 0x00;
+ ptToHost->cRegisterID = 0x00;
+ ptToHost->cRegisterValue = 0x00;
+ ptToHost->cMMaddress = 0x00;
+ ptToHost->cMMdataH = 0x00;
+ ptToHost->cMMdataL = 0x00;
+ } // clear_tToHost.
+/*----------------------------------------------//----------------------------*/
\ No newline at end of file
