]> Pileus Git - ~andy/csm213a-hw/blob - hw2/main_emit.c
Fix event scheduling bug
[~andy/csm213a-hw] / hw2 / main_emit.c
1 #include <stdint.h>
2
3 #include "serial_irq.h"
4 #include "timer_dma.h"
5 #include "main_time.h"
6 #include "main_emit.h"
7
8 /*********************
9  * Signal generation *
10  *********************/
11
12 // for 50 MHz clock  50/1000 = 1/20  (PLL/2)
13
14 // for 48 MHz clock  48/1000 = 6/125 (FLL)
15 // for 24 MHz clock, 24/1000 = 3/125
16 // for 12 MHz clock, 12/1000 = 3/250
17 // for  6 MHz clock,  6/1000 = 3/500
18 // for  3 MHz clock,  3/1000 = 3/1000
19
20 #define EMIT_PS 1
21
22 //#if EMIT_PS == 0
23 //#define EMIT_CLOCKS(nsec) ((uint16_t)((nsec)   / 20))
24 //#define EMIT_NSEC(clocks) ((uint16_t)((clocks) * 20))
25
26 #if EMIT_PS == 0
27 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 6 / 125))
28 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 125 / 6))
29 #elif EMIT_PS == 1
30 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 125))
31 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 125 / 3))
32 #elif EMIT_PS == 2
33 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 250))
34 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 250 / 3))
35 #elif EMIT_PS == 3
36 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 500))
37 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 500 / 3))
38 #elif EMIT_PS == 4
39 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 1000))
40 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 1000 / 3))
41 #endif
42
43 #define EMIT_SLACK_CLOCKS 0x4000  // tune based on emit_worst
44
45 static uint32_t *emit_pcr    = 0; // transmit pin name
46
47 static uint64_t  emit_start  = 0; // transmit start time (world time)
48 static uint64_t  emit_period = 0; // transmit period
49 static uint64_t  emit_due    = 0; // next transmit (world time)
50
51 static uint32_t  emit_slack  = 0; // how far ahead we need to schedule, in us
52 static uint32_t  emit_worst  = 0; // worst-case latency in task table
53
54 static void emit_setup(void)
55 {
56         //        start-.  .-phase-.--period--.
57         //      |       @  |       @  |       @  |
58         //  utc-^                last-^ now-^
59         if (emit_period) {
60                 uint64_t now   = time_to_world(tdma_time());
61                 uint64_t phase = emit_start % emit_period;
62                 uint64_t last  = (now / emit_period) * emit_period;
63                 emit_due = last + phase;
64                 time_printf("emit scheduled", emit_due);
65         } else {
66                 emit_due = 0;
67         }
68 }
69
70 void emit_init(int alt, PinName pin, PinMode mode)
71 {
72         // Calculate slack time
73         emit_slack  = EMIT_NSEC(EMIT_SLACK_CLOCKS);
74
75         // Find pin
76         emit_pcr = (uint32_t*)(PORTA_BASE + pin);
77
78         // Enable clocks
79         SIM->SCGC6            |= SIM_SCGC6_TPM1_MASK;
80         SIM->SOPT2            |= SIM_SOPT2_TPMSRC(1);
81
82         // Reset PLL Source
83         //SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK;
84
85         // Debug print on SOPT2
86         // -- mbed may set PLLFLL when configuring UART0
87         // SOPT2: u0src=1 tpmsrc=1 USBSRC PLL/2 clkos=0 rtcos
88         sirq_printf("SOPT2: u0src=%d tpmsrc=%d %s %s clkos=%d %s\r\n",
89                 (SIM->SOPT2 & SIM_SOPT2_UART0SRC_MASK)     >> SIM_SOPT2_UART0SRC_SHIFT,
90                 (SIM->SOPT2 & SIM_SOPT2_TPMSRC_MASK)       >> SIM_SOPT2_TPMSRC_SHIFT,
91                 (SIM->SOPT2 & SIM_SOPT2_UART0SRC_MASK)     ? "USBSRC" : "usbsrc",
92                 (SIM->SOPT2 & SIM_SOPT2_PLLFLLSEL_MASK)    ? "PLL/2"  : "FLL",
93                 (SIM->SOPT2 & SIM_SOPT2_CLKOUTSEL_MASK)    >> SIM_SOPT2_CLKOUTSEL_SHIFT,
94                 (SIM->SOPT2 & SIM_SOPT2_RTCCLKOUTSEL_MASK) ? "RTCOS"  : "rtcos");
95
96         // Set pin mode
97         emit_pcr[0]            = PORT_PCR_ISF_MASK
98                                | PORT_PCR_MUX(alt)
99                                | mode;
100
101         // Setup Timer/PWM Module
102         TPM1->SC               = TPM_SC_TOF_MASK;
103         TPM1->CNT              = TPM_CNT_COUNT(0);
104         TPM1->MOD              = TPM_MOD_MOD(0xFFFF);
105
106         TPM1->CONTROLS[0].CnSC = TPM_CnSC_CHF_MASK    // clear flag
107                                | TPM_CnSC_MSB_MASK    // set output high on match,
108                                | TPM_CnSC_ELSB_MASK   // cleared on overflow
109                                | TPM_CnSC_ELSA_MASK;  // ..
110
111         TPM1->STATUS           = TPM_STATUS_CH0F_MASK
112                                | TPM_STATUS_TOF_MASK;
113
114         TPM1->CONF             = TPM_CONF_CSOO_MASK;
115 }
116
117 void emit_set_start(uint64_t start)
118 {
119         emit_start  = start;
120         emit_setup();
121 }
122
123 void emit_set_period(uint64_t period)
124 {
125         emit_period = period;
126         emit_setup();
127 }
128
129 void emit_schedule(uint64_t when)
130 {
131         uint64_t local  = time_to_local(when) * 3 / 125;
132         uint32_t width  = EMIT_CLOCKS(10000);
133
134         // Disable timer
135         TPM1->SC               = TPM_SC_TOF_MASK;
136
137         __disable_irq();
138
139         uint64_t now    = ((uint64_t)~PIT->LTMR64H << 32)
140                         | ((uint64_t)~PIT->LTMR64L);
141         uint32_t delta  = local - now;
142         uint32_t start  = delta >> (EMIT_PS-1); // convert to clocks
143         uint32_t stop   = start + width;        // end time
144
145         // Set transmit time
146         TPM1->CONTROLS[0].CnV  = start;
147         TPM1->MOD              = TPM_MOD_MOD(stop);
148
149         // Start the timer
150         TPM1->SC               = TPM_SC_TOF_MASK
151                                | TPM_SC_PS(EMIT_PS)
152                                | TPM_SC_CMOD(1);
153
154         __enable_irq();
155
156         // Test
157         //int64_t  cnv = TPM1->CONTROLS[0].CnV;
158         //int64_t  mod = TPM1->MOD;
159         //int64_t  due = local - tdma_time();
160         //sirq_printf("%6d -- cnv=%04x mod=%04x due=%04x start=%04x\r\n",
161         //              (int)(cnv - EMIT_CLOCKS(due)),
162         //              (int)cnv, (int)mod,
163         //              (int)EMIT_CLOCKS(due), EMIT_CLOCKS(start));
164
165         // Clock testing
166         //uint32_t test_tpm0 =  TPM1->CNT;
167         //uint32_t test_pit0 = ~PIT->CHANNEL[0].CVAL;
168         //for (int i = 0; i < 100; i++)
169         //      asm("nop");
170         //uint32_t test_tpm1 =  TPM1->CNT;
171         //uint32_t test_pit1 = ~PIT->CHANNEL[0].CVAL;
172
173         //uint32_t test_tpm  = test_tpm1 - test_tpm0;
174         //uint32_t test_pit  = test_pit1 - test_pit0;
175         //sirq_printf("pit/tpm: %d - tpm=%08x/%08x=%d pit=%08x/%08x=%d\r\n",
176         //              test_tpm - test_pit,
177         //              test_tpm0, test_tpm1, test_tpm,
178         //              test_pit0, test_pit1, test_pit);
179
180         // Debug output
181         //time_printf("emitting event", when);
182 }
183
184 void emit_transmit(uint64_t local, uint64_t world)
185 {
186         static uint64_t prev = 0;
187
188         // Record how how much time we have to reschedule
189         if (prev && (local-prev) > emit_worst)
190                 emit_worst = (local-prev);
191         prev = local;
192
193         // Schedule task if needed
194         if (emit_period && world+emit_slack > emit_due) {
195                 emit_schedule(emit_due);
196                 emit_due += emit_period;
197         }
198         if (emit_period && emit_due < world) {
199                 sirq_printf("missed emit deadline\r\n");
200                 time_printf("  due   ", emit_due);
201                 time_printf("  period", emit_period);
202                 time_printf("  world ", world);
203                 emit_setup();
204         }
205 }