Procházet zdrojové kódy

Working timer A0 PWM and interrupts

Min před 4 roky
rodič
revize
03475467ac

+ 1 - 1
emulator/devices/cpu/registers.c

@@ -193,7 +193,7 @@ void cpu_step(Emulator *emu) {
     handle_port_1(emu);
     handle_usci(emu);
     handle_interrupts(emu);
-    cpu->nsecs += cpu->bcm->mclk_period;
+    cpu->nsecs += cpu->bcm->cpu_period;
 }
 void cpu_reset(Emulator *emu) {
     Cpu *cpu = emu->cpu;

+ 2 - 0
emulator/devices/cpu/registers.h

@@ -26,6 +26,8 @@
 
 #include "../../emulator.h"
 
+#define CLK_PER_INSTR 4.883;  // Approximation
+
 enum {
   P1_0_ON_PACKET  = 0x00,
   P1_0_OFF_PACKET = 0x01,

+ 17 - 15
emulator/devices/peripherals/bcm.c

@@ -34,36 +34,36 @@ void handle_bcm (Emulator *emu)
   uint8_t DIVMx = (BCSCTL2 >> 4) & 0x03;
 
   if (SELMx == 0b00 || SELMx == 0b01) { // source = DCOCLK
-    bcm->mclk_source = DCOCLK;
-    bcm->mclk_freq = (bcm->dco_freq*1.0) / bcm->mclk_div;
+    bcm->mclk_freq = (bcm->dco_freq*1.0) / (1 << DIVMx);
     bcm->mclk_period = (1.0/(bcm->mclk_freq))*1000000000.0;
+    bcm->cpu_period = bcm->mclk_period * CLK_PER_INSTR;
   }
   else if (SELMx == 0b10) { // XT2CLK
-    bcm->mclk_source = XT2CLK;
     bcm->mclk_freq = 0;
     bcm->mclk_period = 0;
   }
-  else if (SELMx == 0b11) { // VLOCLK
-    bcm->mclk_source = VLOCLK;
-    bcm->mclk_freq = 12000 / bcm->mclk_div;
+  else if (SELMx == 0b11) { // LFXT1CLK
+    bcm->mclk_freq = (bcm->lfxt1_freq*1.0) / (1 << DIVMx);
     bcm->mclk_period = (1.0/(bcm->mclk_freq))*1000000000.0;
-  }
-
-  switch (DIVMx) {
-  case 0b00: bcm->mclk_div = 1; break;
-  case 0b01: bcm->mclk_div = 2; break;
-  case 0b10: bcm->mclk_div = 4; break;
-  case 0b11: bcm->mclk_div = 8; break;
-  default: break;
+    bcm->cpu_period = bcm->mclk_period * CLK_PER_INSTR;
   }
 
   // HANDLE SMCLK -------------------
   uint8_t SELS  = (BCSCTL2 >> 3) & 0x01;
   uint8_t DIVSx = (BCSCTL2 >> 1) & 0x03;
+  if (SELS == 0) { // DCOCLK
+    bcm->smclk_freq = (bcm->dco_freq*1.0) / (1 << DIVSx);
+    bcm->smclk_period = (1.0/(bcm->smclk_freq))*1000000000.0;
+  }
+  else if (SELS == 1) { // XT2CLK or LFXT1CLK
+    bcm->smclk_freq = (bcm->lfxt1_freq*1.0) / (1 << DIVSx);
+    bcm->smclk_period = (1.0/(bcm->smclk_freq))*1000000000.0;
+  }
 
   // HANDLE ACLK -------------------
   uint8_t DIVAx = (BCSCTL1 >> 4) & 0x03;
-  
+  bcm->aclk_freq = bcm->lfxt1_freq / (1 << DIVAx);
+  bcm->aclk_period = (1.0/(bcm->aclk_freq))*1000000000.0;
 
   // HANDLE LOW POWER MODES --------
 
@@ -166,6 +166,8 @@ void setup_bcm (Emulator *emu)
   bcm->dco_freq = 1030000;
   bcm->dco_period = 971;
   bcm->dco_pulse_width = 970 / 2;
+
+  bcm->lfxt1_freq = 2000; // measured internal frequency
 }
 
 

+ 10 - 3
emulator/devices/peripherals/bcm.h

@@ -49,15 +49,22 @@ struct Bcm {
   uint8_t *IFG1;    // r/w, @0x2, PUC: 0x0
 
   // -----
+  uint64_t lfxt1_freq;  // In Hz
+
   uint64_t dco_freq; // In Hz
   uint64_t dco_period; // In nanosecs  
   uint64_t dco_pulse_width; // In nanosecs  
 
   // ----
-  uint8_t mclk_source;
-  uint64_t mclk_div;
+  uint64_t aclk_freq;
+  uint64_t aclk_period; // in nanosecs
+
+  uint64_t smclk_freq;
+  uint64_t smclk_period; // in nanosecs
+
   uint64_t mclk_freq;
-  uint64_t mclk_period; // in manosecs
+  uint64_t mclk_period; // in nanosecs
+  uint64_t cpu_period;  // average instruction period in nanosecs
 };
 
 //uint64_t nanosec_diff(struct timespec *timeA_p, struct timespec *timeB_p);

+ 132 - 66
emulator/devices/peripherals/timer_a.c

@@ -33,6 +33,7 @@ void handle_timer_a (Emulator *emu)
   uint8_t TA0CLR   = (TA0CTL >> 2) & 0x01;
   uint8_t TA0IE    = (TA0CTL >> 1) & 0x01;
   uint8_t TA0IFG   = TA0CTL & 0x01;
+  uint64_t freq = 0;
 
   switch (TASSEL0) {
   case 0b00: {timer->source_0 = TACLK; break;}
@@ -50,6 +51,9 @@ void handle_timer_a (Emulator *emu)
   default: break;
   }
 
+  if(timer->source_0 == ACLK) freq = cpu->bcm->aclk_freq / timer->idiv_0;
+  else if(timer->source_0 == SMCLK) freq = cpu->bcm->smclk_freq / timer->idiv_0;
+
   switch (MC0) {
   case 0b00: {timer->mode_0 = STOP_MODE; break;}
   case 0b01: {timer->mode_0 = UP_MODE; break;}
@@ -70,104 +74,157 @@ void handle_timer_a (Emulator *emu)
   // --------------------------------------------------------------
   // Handle Timer_A0 Capture/Compare Control Register
   
+  uint16_t TA0CCTL0 = *timer->TA0CCTL0;
   uint16_t TA0CCTL1 = *timer->TA0CCTL1;
- 
-  uint8_t OUTMOD1 = (TA0CCTL1 >> 5) & 0x07;  
-  uint8_t CAP     = (TA0CCTL1 >> 8) & 0x01;
-
-  switch (OUTMOD1) {
-  case 0b000: {break;}
-  case 0b001: {break;}
-  case 0b010: {break;}
-  case 0b011: {break;}
-  case 0b100: {break;}
-  case 0b101: {break;}
-  case 0b110: {break;}
-  case 0b111: {break;}
-  default: break;
-  }  
+
+  uint8_t OUTMOD1 = (TA0CCTL1 >> 5) & 0x07;
+  uint8_t CCIE    = (TA0CCTL0 >> 4) & 0x01;
+  uint8_t CAP     = (TA0CCTL0 >> 8) & 0x01;
 
   // CAP field (Capture or compare mode?)
   timer->capture_mode_0 = CAP;
   timer->compare_mode_0 = !CAP;
 
   // --------------------------------------------------------------
+  if(timer->timer_0_running) {
+    uint64_t period = 1000000000 / freq;
+    uint64_t value = (cpu->nsecs - timer->timer_0_start) / period;
+    uint16_t cmp;
+    if(timer->mode_0 == UP_MODE || timer->mode_0 == CONTINOUS_MODE) {
+        cmp = (timer->mode_0 == UP_MODE) ? *timer->TA0CCR0 : 0xFFFF;
+        if(value > cmp) {
+            value = value % cmp;
+            timer->timer_0_start = cpu->nsecs;
+            if(CCIE) service_interrupt(emu, TIMER0_A0_VECTOR);
+        }
+        timer->timer_0_freq = (freq * 1.0) / cmp;
+    }
+    else if(timer->mode_0 == UP_DOWN_MODE) {
+        cmp = *timer->TA0CCR0;
+        if(value > *timer->TA0CCR0 * 2) {
+            value = value % (*timer->TA0CCR0 * 2);
+            timer->timer_0_start = cpu->nsecs;
+        } else if(value > *timer->TA0CCR0) {
+            if(*timer->TA0R < value && CCIE) service_interrupt(emu, TIMER0_A0_VECTOR);
+            value = *timer->TA0CCR0 - (value % *timer->TA0CCR0);
+        }
+        timer->timer_0_freq = (freq * 2.0) / cmp;
+    }
+
+    switch (OUTMOD1) {
+    case 0b000: {  // 0: Output
+      timer->timer_0_duty = 0;
+      break;
+    }
+    case 0b001: {  // 1: Set
+      timer->timer_0_duty = (timer->timer_0_duty == 0.0 && value < cmp) ? 0.0 : 1.0;
+      break;
+    }
+    case 0b010:    // 2: Toggle/Reset
+    case 0b011: {  // 3: Set/Reset
+      if(timer->mode_0 == UP_MODE) timer->timer_0_duty = 1.0 - *timer->TA0CCR1 / (double)cmp;
+      else timer->timer_0_duty = 1.0 - abs(*timer->TA0CCR1 - *timer->TA0CCR0) / (double)cmp;
+      break;
+    }
+    case 0b100: {  // 4: Toggle
+      timer->timer_0_duty = 0.5;
+      break;
+    }
+    case 0b101: {  // 5: Reset
+      timer->timer_0_duty = (timer->timer_0_duty == 1.0 && value < cmp) ? 1.0 : 0.0;
+      break;
+    }
+    case 0b110:    // 6: Toggle/Set
+    case 0b111: {  // 7: Reset/Set
+      double x;
+      if(timer->mode_0 == UP_MODE) x = *timer->TA0CCR1 / (double)cmp;
+      else x = abs(*timer->TA0CCR1 - *timer->TA0CCR0) / (double)cmp;
+      timer->timer_0_duty = x;
+      break;
+    }
+    default: break;
+   }
 
+    *timer->TA0R = (uint16_t)value;
+  }
 
+/*
   static double last_period = 0;
   static double last_pulse_width = 0;
 
   // Figure Out Frequency in up mode
+
   if (timer->compare_mode_0) {
     if (timer->mode_0 == UP_MODE) {
-      uint16_t period_ct = *timer->TA0CCR0 + 1;       
+      uint16_t period_ct = *timer->TA0CCR0 + 1;
       uint64_t frequency = emu->cpu->bcm->mclk_freq;
 
       double period = (1.0/frequency) * period_ct;// In seconds
       double pulse_width = (1.0/frequency) * (*timer->TA0CCR1);
-      double duty_cycle  = pulse_width / period; 
-      
-      /*
-	printf("period: %lf\npulse_width: %lf\nduty: %lf%%\n", 
-	period, pulse_width, duty_cycle);
-      */
-      
+      double duty_cycle  = pulse_width / period;
+
+
+//	printf("period: %lf\npulse_width: %lf\nduty: %lf%%\n",
+//	period, pulse_width, duty_cycle);
+
       if (last_period != period || last_pulse_width != pulse_width) {
-	if (period >= 0.015 && period <= 0.025) { // 0.020 is sweet spot 50 Hz
-	  //printf("period: %lf, last_period: %lf\n", period, last_period);
-	  //printf("pw: %lf, last_pw: %lf\n", pulse_width, last_pulse_width);
-
-	  if (pulse_width < 0.0009) {
-	    // Send Control for 0 degrees
-	    //print_console(emu, "0 degrres\n");
-	    
-	    uint8_t byte = 0;
-	    send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
-	  }
-	  else if (pulse_width < 0.0012) {
-	    // Send Control for 30 Degrees
-	    //print_console(emu, "30 degrres\n");
-	    
-	    uint8_t byte = 30;
-	    send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
-	  }
-	  else if (pulse_width < 0.0015) {
-	    // Send Control for 60 degrees
-	    uint8_t byte = 60;
-	    send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
-	  }
-	  else if (pulse_width < 0.0018) {
-	    // Send Control for 90 degrees
-	    //print_console(emu, "90 degrres\n");
-	    
-	    uint8_t byte = 90;
-	    send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
-	  }
-	  else if (pulse_width < 0.0021) {
-	    // Send Control for 120 degrees
-	    uint8_t byte = 120;
-	    send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
-	  }
-	  else if (pulse_width >= 0.0021) {
-	    // Send Control for 150 degrees
-	    uint8_t byte = 150;
-	    send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
-	  }
-	}
+        if (period >= 0.015 && period <= 0.025) { // 0.020 is sweet spot 50 Hz
+          //printf("period: %lf, last_period: %lf\n", period, last_period);
+          //printf("pw: %lf, last_pw: %lf\n", pulse_width, last_pulse_width);
+
+          if (pulse_width < 0.0009) {
+            // Send Control for 0 degrees
+            //print_console(emu, "0 degrres\n");
+
+            uint8_t byte = 0;
+            send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
+          }
+          else if (pulse_width < 0.0012) {
+            // Send Control for 30 Degrees
+            //print_console(emu, "30 degrres\n");
+
+            uint8_t byte = 30;
+            send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
+          }
+          else if (pulse_width < 0.0015) {
+            // Send Control for 60 degrees
+            uint8_t byte = 60;
+            send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
+          }
+          else if (pulse_width < 0.0018) {
+            // Send Control for 90 degrees
+            //print_console(emu, "90 degrres\n");
+
+            uint8_t byte = 90;
+            send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
+          }
+          else if (pulse_width < 0.0021) {
+            // Send Control for 120 degrees
+            uint8_t byte = 120;
+            send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
+          }
+          else if (pulse_width >= 0.0021) {
+            // Send Control for 150 degrees
+            uint8_t byte = 150;
+            send_control(emu, SERVO_MOTOR, (void *)&byte, 1);
+          }
+        }
       }
 
       if (OUTMOD1 == 0b111) { // RESET/SET
-	uint16_t pulse_width = *timer->TA0CCR1;	
+	    uint16_t pulse_width = *timer->TA0CCR1;
       }
-
       last_period = period;
       last_pulse_width = pulse_width;
     }
   }
+  */
 
   if (!timer->timer_0_running && MC0 != 0) {
     //print_console(emu, "START TIMER\n");
+//    puts("TimerA0 started");
     timer->timer_0_running = true;
+    timer->timer_0_start = cpu->nsecs;
 
     //pthread_t pp;
     //if(pthread_create(&pp, NULL, timer_A0_thread , (void*)emu)){
@@ -175,6 +232,12 @@ void handle_timer_a (Emulator *emu)
     //exit(1);
     //}
   }
+  if(timer->timer_0_running && MC0 == 0) {
+//    puts("TimerA0 stopped");
+    timer->timer_0_running = false;
+    timer->timer_0_freq = 0;
+    timer->timer_0_duty = 0;
+  }
 }
 
 void *timer_A0_thread (void *data)
@@ -258,6 +321,9 @@ void setup_timer_a (Emulator *emu)
   // Configure other
   timer->source_0 = 0b10;
   timer->timer_0_running = false;
+  timer->timer_0_start = 0;
+  timer->timer_0_freq = 0.0;
+  timer->timer_0_duty = 0.0;
 
   timer->source_1 = 0b10;
   timer->timer_1_running = false;

+ 3 - 0
emulator/devices/peripherals/timer_a.h

@@ -89,6 +89,9 @@ struct Timer_a {
   uint8_t source_0;
   uint8_t idiv_0;
   uint8_t mode_0;
+  uint64_t timer_0_start;
+  double timer_0_freq;  // in Hz
+  double timer_0_duty; // duty cycle from 0 to 1
 
   bool timer_1_running;
   bool capture_mode_1;

+ 3 - 3
emulator/devices/peripherals/usci.c

@@ -124,10 +124,10 @@ void handle_usci (Emulator *emu) {
     uint64_t clock = 0;
     usci->UART_baud = 0;
     usci->USCI_RESET = false;
-    if((*usci->UCA0CTL1 & 0xc0) == 0x40) { // USCI clock source is ACLK
-        clock = 32768;
+    if((*usci->UCA0CTL1 & 0xc0) == 0x40) { // ACLK
+        clock = cpu->bcm->aclk_freq;
     } else if ((*usci->UCA0CTL1 & 0x80) == 0x80) { // SMCLK
-        clock = cpu->bcm->mclk_freq;
+        clock = cpu->bcm->smclk_freq;
     }
     if(clock > 0) {
         double baud = clock / *usci->UCA0BR0;

+ 9 - 13
emulator/python/py_functions.c

@@ -67,17 +67,13 @@ PyObject *get_misc_data() {
     PyObject *dict = PyDict_New();
     PyDict_SetItemString(dict, "period", PyLong_FromUnsignedLongLong(cpu->nsecs));
     PyDict_SetItemString(dict, "mclk", PyLong_FromUnsignedLongLong(bcm->mclk_freq));
-
-    PyObject *mck_src_str;
-    switch(bcm->mclk_source) {
-        case DCOCLK: {mck_src_str = PyUnicode_FromString("DCOCLK"); break;}
-        case XT2CLK: {mck_src_str = PyUnicode_FromString("XT2CLK"); break;}
-        case VLOCLK: {mck_src_str = PyUnicode_FromString("VLOCLK"); break;}
-        default: {mck_src_str = PyUnicode_FromString("?"); break;}
-//         TACLK, ACLK, SMCLK, MCLK, INCLK, NUM_CLOCKS
-    }
-    PyDict_SetItemString(dict, "mclk_src", mck_src_str);
+    PyDict_SetItemString(dict, "aclk", PyLong_FromUnsignedLongLong(bcm->aclk_freq));
+    PyDict_SetItemString(dict, "smclk", PyLong_FromUnsignedLongLong(bcm->smclk_freq));
     PyDict_SetItemString(dict, "uart_baud", PyLong_FromUnsignedLong(cpu->usci->UART_baud));
+    PyDict_SetItemString(dict, "timer_a0_freq", PyFloat_FromDouble(cpu->timer_a->timer_0_freq));
+    PyDict_SetItemString(dict, "timer_a0_duty", PyFloat_FromDouble(cpu->timer_a->timer_0_duty));
+    uint8_t pwm_pins = ~*cpu->p1->_OUT & *cpu->p1->_SEL & ~*cpu->p1->_SEL2;
+    PyDict_SetItemString(dict, "timer_pwm_pins", PyBytes_FromStringAndSize(&pwm_pins, 1));
 
     return dict;
 }
@@ -126,7 +122,7 @@ PyObject *get_bcm_regs() {
 
 PyObject *get_timer_regs() {
     if(emuInst == NULL) return Py_None;
-    char regs[18];
+    uint16_t regs[18];
     Timer_a *timer = emuInst->cpu->timer_a;
     regs[0] = *timer->TA0CTL;
     regs[1] = *timer->TA0R;
@@ -148,7 +144,7 @@ PyObject *get_timer_regs() {
     regs[16] = *timer->TA1CCR2;
     regs[17] = *timer->TA1IV;
 
-    return PyBytes_FromStringAndSize(regs, 18);
+    return PyBytes_FromStringAndSize((char*)regs, 18*2);
 }
 
 PyObject *get_usci_regs() {
@@ -301,7 +297,7 @@ void start_emu(char *file) {
                 if(cpu->bcm->mclk_period == 0) break;
 
                 // Average of 4 cycles per instruction
-                uint64_t cycles_time = (uint64_t)(cpu->bcm->mclk_period * 4.883 * 500);
+                uint64_t cycles_time = cpu->bcm->cpu_period * 500;
                 uint64_t delta = time_now - time_last;
                 if(time_last > time_now) delta = 0;
                 uint64_t sleep_time = cycles_time - delta;

+ 106 - 36
msp430emu/emulator.py

@@ -183,8 +183,8 @@ class EmulatorWindow(wx.Frame):
             "Clear console": (view_menu, 0, wx.NewId(), "Clean text in console window", lambda e: self.control.Clear()),
 
             "Step\tCtrl+N": (debug_menu, 0, wx.NewId(), "Step single instruction", self.OnStep),
-            "Registers": (debug_menu, 0, wx.NewId(), "Print registers in console", lambda e: self.emu.send_command("regs")),
-            "Misc": (debug_menu, 0, wx.NewId(), "", lambda e: print(self.emu.get_misc_info())),
+            # "Registers": (debug_menu, 0, wx.NewId(), "Print registers in console", lambda e: self.emu.send_command("regs")),
+            # "Misc": (debug_menu, 0, wx.NewId(), "", lambda e: print(self.emu.get_misc_info())),
         }
 
         for name, (menu, typ, wx_id, desc, action) in self.menu_navigation.items():
@@ -239,13 +239,17 @@ class EmulatorWindow(wx.Frame):
         self.sizer0.Add(self.serial, 1, wx.EXPAND)
         self.sizer0.Add(self.sizer3, 0, wx.EXPAND)
 
-        panel = wx.Panel(self, size=wx.Size(275, 375))
+        self.diagram_panel = wx.Panel(self, size=wx.Size(275, 375))
         # img =
         # wx.StaticBitmap(panel, -1, img, (0, 0), (img.GetWidth(), img.GetHeight()))
-        self.diagram = DrawRect(panel, -1, size=wx.Size(275, 375))
+        self.diagram = DrawRect(self.diagram_panel, -1, size=wx.Size(275, 375))
 
         self.sizer_diagram = wx.BoxSizer(wx.VERTICAL)
-        self.sizer_diagram.Add(panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)
+        self.sizer_diagram.Add(self.diagram_panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)
+
+        self.misc_label = wx.StaticText(self, label="")
+        self.misc_label.SetBackgroundColour(wx.Colour(0, 0, 0, alpha=0))
+        self.sizer_diagram.Add(self.misc_label, 0, wx.ALIGN_TOP)
 
         self.registers = RegisterPanel(self, self.emu)
 
@@ -267,6 +271,7 @@ class EmulatorWindow(wx.Frame):
         self.btn_send_serial.Disable()
 
         self.emu_paused = True
+        self.emu_misc_info = None
         self.timer_running = Event()
         self.timer = Thread(target=self.OnTimer)
         self.timer.start()
@@ -320,9 +325,40 @@ class EmulatorWindow(wx.Frame):
             self.load = fileDialog.GetPath()
             self.OnLoad(None)
 
+    def UpdateMisc(self):
+        info = self.emu.get_misc_info()
+        if info is not None:
+            text = f"Time: {info.get('period', 0) / 1e9 :.3f}s"
+            text += f"\nMCLK: {info.get('mclk', 0) / 1e6 :.3f}MHz"
+            text += f"\nACLK: {info.get('aclk', 0) / 1e3 :.2f}kHz"
+            text += f"\nSMCLK: {info.get('smclk', 0) / 1e6 :.3f}MHz"
+            if info.get('uart_baud', 0) > 0:
+                text += f"\nUART Baud: {info['uart_baud']}"
+            ta0_freq = info.get('timer_a0_freq', 0)
+            if ta0_freq > 0:
+                pins = info.get('timer_pwm_pins', [0])[0]
+                duty = info.get('timer_a0_duty', 0) * 100
+                if ta0_freq > 1:
+                    text += f"\nTA0 PWM: {ta0_freq:.1f}Hz"
+                else:
+                    text += f"\nTA0 PWM: {1.0/ta0_freq:.2f}s"
+                text += f" {duty:.2f}%"
+                if self.diagram.pwm_pins != pins or \
+                        self.diagram.ta0_freq != ta0_freq or \
+                        self.diagram.ta0_duty != duty:
+                    self.diagram.pwm_pins = pins
+                    self.diagram.ta0_freq = ta0_freq
+                    self.diagram.ta0_duty = duty
+                    wx.CallAfter(self.diagram.Refresh)
+            wx.CallAfter(self.misc_label.SetLabel, text)
+            if self.misc_label.GetLabel().count('\n') != text.count('\n'):
+                wx.CallAfter(self.sizer_diagram.Layout)
+            self.emu_misc_info = info
+
     def OnLoad(self, e):
         if self.load is None:
             return
+        self.misc_label.SetLabel("")
         self.emu.load_file(self.load)
         self.diagram.reset()
         self.btn_key.Disable()
@@ -348,6 +384,7 @@ class EmulatorWindow(wx.Frame):
                 pass
             if self.emu_paused:
                 continue
+            self.UpdateMisc()
             port1 = self.emu.get_port1_regs()
             if port1 is not None and self.diagram.port1 != port1[0]:
                 self.diagram.port1 = port1[0]
@@ -426,6 +463,7 @@ class EmulatorWindow(wx.Frame):
     def OnKeyReset(self, e):
         self.diagram.reset()
         self.emu.reset()
+        self.misc_label.SetLabel("")
 
     def SendSerial(self, e):
         text = self.serial_input.GetValue()
@@ -435,6 +473,7 @@ class EmulatorWindow(wx.Frame):
     def OnStep(self, e):
         self.emu.send_command("step")
         self.registers.update_values()
+        self.UpdateMisc()
 
     def ToggleRegisters(self, e):
         if e.Id == self.menu_navigation["Port1 Registers"][2]:
@@ -467,13 +506,13 @@ class RegisterPanel(wx.Panel):
         self.box = wx.BoxSizer(wx.HORIZONTAL)
         self.emu = emu
         self.regs_port1 = {name: None for name in emu.REG_NAMES_PORT1}
-        self.regs_bcm = {name: None for name in emu.REG_NAMES_BCM + ["MCLK"]}
+        self.regs_bcm = {name: None for name in emu.REG_NAMES_BCM}
         self.regs_timer_a = {name: None for name in emu.REG_NAMES_TIMER_A}
         self.regs_usci = {name: None for name in emu.REG_NAMES_USCI}
         self.regs_cpu = {name: None for name in emu.REG_NAMES_CPU}
 
         self.grid_port1 = wx.FlexGridSizer(len(self.regs_port1), 2, 0, 10)
-        self.grid_bmc = wx.FlexGridSizer(len(self.regs_bcm), 2, 0, 10)
+        self.grid_bmc = wx.FlexGridSizer(len(self.regs_bcm), 2, 0, 5)
         self.grid_timer_a = wx.FlexGridSizer(len(self.regs_timer_a), 2, 0, 10)
         self.grid_usci = wx.FlexGridSizer(len(self.regs_usci), 2, 0, 10)
         self.grid_cpu = wx.FlexGridSizer(len(self.regs_cpu), 2, 0, 5)
@@ -522,22 +561,8 @@ class RegisterPanel(wx.Panel):
         values = func()
         if values is None:
             return None
-        if panel == self.panel_bmc:
-            formatted = [f"{value:08b}" for value in values]
-            freq = "x"
-            if values[0] == 96 and values[1] == 135:
-                freq = "1.03"
-            elif values[0] == 0b11010001 and values[1] == 0b10000110:
-                freq = "1.00"
-            elif values[0] == 0b10010010 and values[1] == 0b10001101:
-                freq = "8.00"
-            elif values[0] == 0b10011110 and values[1] == 0b10001110:
-                freq = "12.0"
-            elif values[0] == 0b10010101 and values[1] == 0b10001111:
-                freq = "16.0"
-            formatted.append(freq + " MHz")
-        elif panel == self.panel_cpu:
-            formatted = ["0x%0.4X" % int.from_bytes(values[i:i+2], sys.byteorder) for i in range(0, len(values), 2)]
+        if panel == self.panel_cpu or panel == self.panel_timer_a:
+            formatted = ["0x%0.4X" % int.from_bytes(values[i:i + 2], sys.byteorder) for i in range(0, len(values), 2)]
         else:
             formatted = [f"{value:08b}" for value in values]
         return formatted
@@ -584,6 +609,7 @@ class DrawRect(wx.Panel):
     """ class MyPanel creates a panel to draw on, inherits wx.Panel """
     RED = wx.Colour(255, 0, 0, wx.ALPHA_OPAQUE)
     GREEN = wx.Colour(0, 255, 0, wx.ALPHA_OPAQUE)
+    WHITE = wx.Colour(255, 255, 255, wx.ALPHA_OPAQUE)
 
     def __init__(self, parent, id, **kwargs):
         # create a panel
@@ -592,33 +618,77 @@ class DrawRect(wx.Panel):
         self.Bind(wx.EVT_PAINT, self.OnPaint)
         self.power = False
         self.port1 = 0
+        self.pwm_pins = 0
+        self.ta0_freq = 0
+        self.ta0_duty = 0
         self.image = wx.Bitmap(path.join(source_dir, "msp430.png"), wx.BITMAP_TYPE_PNG)
 
+    @staticmethod
+    def hsv_to_rgb(h, s, v):
+        i = int(h * 6.)
+        f = (h * 6.) - i
+        v *= 255
+        p, q, t = v * (1. - s), v * (1. - s * f), v * (1. - s * (1. - f))
+        i %= 6
+        if i == 0:
+            return v, t, p
+        if i == 1:
+            return q, v, p
+        if i == 2:
+            return p, v, t
+        if i == 3:
+            return p, q, v
+        if i == 4:
+            return t, p, v
+        if i == 5:
+            return v, p, q
+
     def reset(self):
         self.power = False
         self.port1 = 0
+        self.pwm_pins = 0
+        self.ta0_freq = 0
+        self.ta0_duty = 0
         wx.CallAfter(self.Refresh)
 
     def OnPaint(self, evt):
         """set up the device context (DC) for painting"""
         self.dc = wx.PaintDC(self)
         self.dc.DrawBitmap(self.image, 0, 0, True)
-        if self.power:
-            self.dc.SetPen(wx.Pen(self.GREEN, style=wx.TRANSPARENT))
-            self.dc.SetBrush(wx.Brush(self.GREEN, wx.SOLID))
-            # set x, y, w, h for rectangle
-            self.dc.DrawRectangle(39, 110, 8, 15)
 
-        if self.port1 & 1 << 6:
-            self.dc.SetPen(wx.Pen(self.GREEN, style=wx.TRANSPARENT))
-            self.dc.SetBrush(wx.Brush(self.GREEN, wx.SOLID))
-            # set x, y, w, h for rectangle
-            self.dc.DrawRectangle(83, 356, 8, 15)
+        # GREEN Power LED
+        colour = self.GREEN if self.power else self.WHITE
+        self.dc.SetPen(wx.Pen(colour, style=wx.TRANSPARENT))
+        self.dc.SetBrush(wx.Brush(colour, wx.SOLID))
+        # set x, y, w, h for rectangle
+        self.dc.DrawRectangle(39, 110, 8, 15)
 
+        # GREEN LED
+        if self.port1 & 1 << 6:
+            v = 1.0
+        elif self.pwm_pins & 1 << 6:
+            v = (self.ta0_duty+20) / 120
+        else:
+            v = 0.0
+        colour = wx.Colour(*self.hsv_to_rgb(1 / 3, v, 1), alpha=255)
+        self.dc.SetPen(wx.Pen(colour, style=wx.TRANSPARENT))
+        self.dc.SetBrush(wx.Brush(colour, wx.SOLID))
+        # set x, y, w, h for rectangle
+        self.dc.DrawRectangle(83, 356, 8, 15)
+
+        # RED LED
         if self.port1 & 1:
-            self.dc.SetPen(wx.Pen(self.RED, style=wx.TRANSPARENT))
-            self.dc.SetBrush(wx.Brush(self.RED, wx.SOLID))
-            self.dc.DrawRectangle(70, 356, 8, 15)
+            v = 1.0
+        elif self.pwm_pins & 1:
+            v = (self.ta0_duty+20) / 120
+        else:
+            v = 0.0
+        colour = wx.Colour(*self.hsv_to_rgb(0, v, 1), alpha=255)
+        self.dc.SetPen(wx.Pen(colour, style=wx.TRANSPARENT))
+        self.dc.SetBrush(wx.Brush(colour, wx.SOLID))
+        # set x, y, w, h for rectangle
+        self.dc.DrawRectangle(70, 356, 8, 15)
+
         del self.dc
 
 

+ 2 - 2
readme.md

@@ -28,8 +28,8 @@ List of features that are added:
 - [x] Reset button
 - [x] Button for P1.3
 - [x] Interrupts
-- [ ] Timer A
-- [ ] PWM Signals
+- [x] Timer A
+- [x] PWM Signals
 - [x] Unbiased circuit simulation (due to internal pull-up/down resistors)
 - [ ] Watchdog Timer
 - [ ] GPIO Oscilloscope