]> Pileus Git - ~andy/csm213a-hw/blob - hw2/main_emit.c
Split start and period set functions
[~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   = 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         } else {
65                 emit_due = 0;
66         }
67
68         time_printf("emit scheduled", emit_due);
69 }
70
71 void emit_init(int alt, PinName pin, PinMode mode)
72 {
73         // Calculate slack time
74         emit_slack  = EMIT_NSEC(EMIT_SLACK_CLOCKS);
75
76         // Find pin
77         emit_pcr = (uint32_t*)(PORTA_BASE + pin);
78
79         // Enable clocks
80         SIM->SCGC6            |= SIM_SCGC6_TPM1_MASK;
81         SIM->SOPT2            |= SIM_SOPT2_TPMSRC(1);
82
83         // Reset PLL Source
84         //SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK;
85
86         // Debug print on SOPT2
87         // -- mbed may set PLLFLL when configuring UART0
88         // SOPT2: u0src=1 tpmsrc=1 USBSRC PLL/2 clkos=0 rtcos
89         sirq_printf("SOPT2: u0src=%d tpmsrc=%d %s %s clkos=%d %s\r\n",
90                 (SIM->SOPT2 & SIM_SOPT2_UART0SRC_MASK)     >> SIM_SOPT2_UART0SRC_SHIFT,
91                 (SIM->SOPT2 & SIM_SOPT2_TPMSRC_MASK)       >> SIM_SOPT2_TPMSRC_SHIFT,
92                 (SIM->SOPT2 & SIM_SOPT2_UART0SRC_MASK)     ? "USBSRC" : "usbsrc",
93                 (SIM->SOPT2 & SIM_SOPT2_PLLFLLSEL_MASK)    ? "PLL/2"  : "FLL",
94                 (SIM->SOPT2 & SIM_SOPT2_CLKOUTSEL_MASK)    >> SIM_SOPT2_CLKOUTSEL_SHIFT,
95                 (SIM->SOPT2 & SIM_SOPT2_RTCCLKOUTSEL_MASK) ? "RTCOS"  : "rtcos");
96
97         // Set pin mode
98         emit_pcr[0]            = PORT_PCR_ISF_MASK
99                                | PORT_PCR_MUX(alt)
100                                | mode;
101
102         // Setup Timer/PWM Module
103         TPM1->SC               = TPM_SC_TOF_MASK;
104         TPM1->CNT              = TPM_CNT_COUNT(0);
105         TPM1->MOD              = TPM_MOD_MOD(0xFFFF);
106
107         TPM1->CONTROLS[0].CnSC = TPM_CnSC_CHF_MASK    // clear flag
108                                | TPM_CnSC_MSB_MASK    // set output highon match,
109                                | TPM_CnSC_ELSB_MASK   // cleared on overflow
110                                | TPM_CnSC_ELSA_MASK;  // ..
111
112         TPM1->STATUS           = TPM_STATUS_CH0F_MASK
113                                | TPM_STATUS_TOF_MASK;
114
115         TPM1->CONF             = TPM_CONF_CSOO_MASK;
116 }
117
118 void emit_set_start(uint64_t start)
119 {
120         emit_start  = start;
121         emit_setup();
122 }
123
124 void emit_set_period(uint64_t period)
125 {
126         emit_period = period;
127         emit_setup();
128 }
129
130 void emit_schedule(uint64_t when)
131 {
132         uint64_t local  = time_to_local(when) * 3 / 125;
133         uint32_t width  = EMIT_CLOCKS(10000);
134
135         // Disable timer
136         TPM1->SC               = TPM_SC_TOF_MASK;
137
138         __disable_irq();
139
140         uint64_t now    = ((uint64_t)~PIT->LTMR64H << 32)
141                         | ((uint64_t)~PIT->LTMR64L);
142         uint32_t delta  = local - now;
143         uint32_t start  = delta >> (EMIT_PS-1); // convert to clocks
144         uint32_t stop   = start + width;        // end time
145
146         // Set transmit time
147         TPM1->CONTROLS[0].CnV  = start;
148         TPM1->MOD              = TPM_MOD_MOD(stop);
149
150         // Start the timer
151         TPM1->SC               = TPM_SC_TOF_MASK
152                                | TPM_SC_PS(EMIT_PS)
153                                | TPM_SC_CMOD(1);
154
155         __enable_irq();
156
157         // Test
158         //int64_t  cnv = TPM1->CONTROLS[0].CnV;
159         //int64_t  mod = TPM1->MOD;
160         //int64_t  due = local - tdma_time();
161         //sirq_printf("%6d -- cnv=%04x mod=%04x due=%04x start=%04x\r\n",
162         //              (int)(cnv - EMIT_CLOCKS(due)),
163         //              (int)cnv, (int)mod,
164         //              (int)EMIT_CLOCKS(due), EMIT_CLOCKS(start));
165
166         // Clock testing
167         //uint32_t test_tpm0 =  TPM1->CNT;
168         //uint32_t test_pit0 = ~PIT->CHANNEL[0].CVAL;
169         //for (int i = 0; i < 100; i++)
170         //      asm("nop");
171         //uint32_t test_tpm1 =  TPM1->CNT;
172         //uint32_t test_pit1 = ~PIT->CHANNEL[0].CVAL;
173
174         //uint32_t test_tpm  = test_tpm1 - test_tpm0;
175         //uint32_t test_pit  = test_pit1 - test_pit0;
176         //sirq_printf("pit/tpm: %d - tpm=%08x/%08x=%d pit=%08x/%08x=%d\r\n",
177         //              test_tpm - test_pit,
178         //              test_tpm0, test_tpm1, test_tpm,
179         //              test_pit0, test_pit1, test_pit);
180
181         // Debug output
182         //time_printf("emitting event", when);
183 }
184
185 void emit_transmit(uint64_t local, uint64_t world)
186 {
187         static uint64_t prev = 0;
188
189         // Record how how much time we have to reschedule
190         if (prev && (local-prev) > emit_worst)
191                 emit_worst = (local-prev);
192         prev = local;
193
194         // Schedule task if needed
195         if (emit_due && emit_period &&
196             world+emit_slack > emit_due) {
197                 emit_schedule(emit_due);
198                 emit_due += emit_period;
199                 if (emit_due < local)
200                         sirq_printf("missed emit deadline\r\n");
201         }
202 }
203