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