]> Pileus Git - ~andy/csm213a-hw/blob - hw2/timer_dma.c
Improve time sync accuracy
[~andy/csm213a-hw] / hw2 / timer_dma.c
1 #include <MKL46Z4.h>\r
2 \r
3 #include <stdint.h>\r
4 #include <stdarg.h>\r
5 #include <stdio.h>\r
6 #include <string.h>\r
7 \r
8 #include "timer_dma.h"\r
9 #include "serial_irq.h"\r
10 \r
11 /* Defines */\r
12 enum {\r
13         TDMA_REQ_PTA  = 49,\r
14         TDMA_REQ_PTC  = 51,\r
15         TDMA_REQ_PTD  = 52,\r
16 };\r
17 \r
18 /* Port structure */\r
19 struct tdma_t {\r
20         /* DMA channel */\r
21         struct {\r
22                 uint32_t sar; // offset 0x00, Source Address Register\r
23                 uint32_t dar; // offset 0x04, Destination Address Register\r
24                 uint32_t dsr; // offset 0x08, DMA Status Register / Byte Count Register\r
25                 uint32_t dcr; // offset 0x0C, DMA Control Register\r
26         } *dma;\r
27 \r
28         /* DMA mux */\r
29         struct {\r
30                 uint8_t  cfg; // offset 0x00, Channel Configuration register\r
31         } *mux;\r
32 \r
33         /* Pin names */\r
34         struct {\r
35                 uint32_t pcr; // offset 0x00, Pin Control register\r
36         } *pin;\r
37 \r
38         /* Time stamping */\r
39         uint32_t time[2];\r
40 \r
41         /* Save state */\r
42         int      req;\r
43         int      irqc;\r
44 };\r
45 \r
46 /* Port data */\r
47 static tdma_t tdma_ports[TDMA_NUM_CHAN];\r
48 \r
49 /* Global timer initialization */\r
50 void tdma_init(void)\r
51 {\r
52         // Enable DMA Cock\r
53         SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK;\r
54         SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK;\r
55         SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK;\r
56         SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;\r
57         SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;\r
58 \r
59         // Enable timer Clock\r
60         SIM->SCGC6 |= SIM_SCGC6_PIT_MASK;\r
61 \r
62         // Enable PIT\r
63         PIT->MCR               = 0;\r
64 \r
65         // Channel 0\r
66         PIT->CHANNEL[0].LDVAL  = 0xFFFFFFFF;\r
67         PIT->CHANNEL[0].TCTRL  = 0;\r
68 \r
69         // Channel 1\r
70         PIT->CHANNEL[1].LDVAL  = 0xFFFFFFFF;\r
71         PIT->CHANNEL[1].TCTRL  = PIT_TCTRL_CHN_MASK;\r
72 \r
73         // Start timers\r
74         PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TEN_MASK;\r
75         PIT->CHANNEL[1].TCTRL |= PIT_TCTRL_TEN_MASK;\r
76 }\r
77 \r
78 /* DMA Functions */\r
79 tdma_t *tdma_open(tdma_chan_t chan, int alt, PinName pin, PinMode mode)\r
80 {\r
81         int req  = pin >= PTD0 ? TDMA_REQ_PTD :\r
82                    pin >= PTC0 ? TDMA_REQ_PTC :\r
83                    pin >= PTA0 ? TDMA_REQ_PTA : 0;\r
84 \r
85         int irqc = mode == PullUp ? 1 : 2;\r
86 \r
87         // Allocate port\r
88         tdma_t *port    = &tdma_ports[chan];\r
89 \r
90         // Setup port pointers\r
91         port->dma       = (void*)&DMA0->DMA[chan];\r
92         port->mux       = (void*)&DMAMUX0->CHCFG[chan];\r
93         port->pin       = (void*)(PORTA_BASE + pin);\r
94 \r
95         // Reset DMA channel\r
96         port->dma->dsr  = DMA_DSR_BCR_DONE_MASK;\r
97 \r
98         // Configure DMA channel\r
99         port->dma->dcr  = DMA_DCR_SINC_MASK         // Source increment\r
100                         | DMA_DCR_DINC_MASK         // Dest increment\r
101                         | DMA_DCR_SSIZE(0)          // 32-bit access\r
102                         | DMA_DCR_DSIZE(0)          // 32-bit access\r
103                         | DMA_DCR_D_REQ_MASK;       // Only run once\r
104 \r
105         // Setup and enable DMA MUX\r
106         port->mux->cfg  = DMAMUX_CHCFG_SOURCE(req)  // Request source\r
107                         | DMAMUX_CHCFG_ENBL_MASK;   // Enable DMA mux\r
108 \r
109         // Set pin to generate DMA req\r
110         port->pin->pcr  = PORT_PCR_ISF_MASK         // Clear ISR flag\r
111                         | PORT_PCR_MUX(alt)         // Pin mapping\r
112                         | PORT_PCR_IRQC(irqc)       // DMA on falling edge\r
113                         | mode;                     // Pin pull up/down\r
114 \r
115         // Save IRC for later\r
116         port->req       = req;\r
117         port->irqc      = irqc;\r
118 \r
119         return port;\r
120 }\r
121 \r
122 void tdma_start(tdma_t *port)\r
123 {\r
124         if (!port)\r
125                 return;\r
126 \r
127         //sirq_printf("isfr2: %08x\r\n", PORTD->ISFR);\r
128 \r
129         // Clear previous time\r
130         port->time[0] = 0;\r
131         port->time[1] = 0;\r
132 \r
133         // Reset DMA Mux\r
134         port->mux->cfg &= DMAMUX_CHCFG_ENBL_MASK;\r
135 \r
136         // Freeze DMA channel\r
137         port->dma->dcr &= ~DMA_DCR_ERQ_MASK;\r
138 \r
139         // Reset DMA channel\r
140         port->dma->dsr  =  DMA_DSR_BCR_DONE_MASK;\r
141 \r
142         // Set addresses and size\r
143         port->dma->sar  =  (uint32_t)&PIT->LTMR64H;  // Global timer\r
144         port->dma->dar  =  (uint32_t)&port->time;    // Temp timer buffer\r
145         port->dma->dsr  =  DMA_DSR_BCR_BCR(8);       // 64-bit timer\r
146 \r
147         // Set pin to generate DMA req\r
148         port->pin->pcr |=  PORT_PCR_ISF_MASK;\r
149         port->pin->pcr |=  PORT_PCR_IRQC(port->irqc);\r
150 \r
151         // Enable port request\r
152         port->dma->dcr |=  DMA_DCR_ERQ_MASK;\r
153 \r
154         // Enable DMA Mux\r
155         port->mux->cfg  = DMAMUX_CHCFG_SOURCE(port->req)\r
156                         | DMAMUX_CHCFG_ENBL_MASK;\r
157 \r
158         //sirq_printf("isfr3: %08x\r\n", PORTD->ISFR);\r
159 }\r
160 \r
161 void tdma_stop(tdma_t *port)\r
162 {\r
163         if (!port)\r
164                 return;\r
165 \r
166         //sirq_printf("isfr0: %08x\r\n", PORTD->ISFR);\r
167 \r
168         // Disable DMA Mux\r
169         port->mux->cfg &= DMAMUX_CHCFG_ENBL_MASK;\r
170 \r
171         // Freeze DMA channel\r
172         port->dma->dcr &= ~DMA_DCR_ERQ_MASK;\r
173 \r
174         // Reset DMA channel\r
175         port->dma->dsr  =  DMA_DSR_BCR_DONE_MASK;\r
176 \r
177         // Disable pin DMA request\r
178         port->pin->pcr &= ~PORT_PCR_IRQC_MASK;\r
179         port->pin->pcr |=  PORT_PCR_ISF_MASK;\r
180 \r
181         //sirq_printf("isfr1: %08x\r\n", PORTD->ISFR);\r
182 }\r
183 \r
184 int tdma_stamp(tdma_t *port, uint64_t *time)\r
185 {\r
186         uint64_t clocks;\r
187 \r
188         if (!port)\r
189                 return 0;\r
190 \r
191         if (port->dma->dsr & DMA_DSR_BCR_BCR_MASK)\r
192                 return 0;\r
193 \r
194         // Read the timestamp\r
195         clocks = ((uint64_t)~port->time[0]) << 32\r
196                | ((uint64_t)~port->time[1]) << 0;\r
197 \r
198         // Convert to nanoseconds\r
199         *time  = clocks * 1000 / 24;\r
200 \r
201         return 1;\r
202 }\r
203 \r
204 uint64_t tdma_time(void)\r
205 {\r
206         uint32_t tmh = PIT->LTMR64H;\r
207         uint32_t tml = PIT->LTMR64L;\r
208 \r
209         // Read the timestamp\r
210         uint64_t clocks = ((uint64_t)~tmh) << 32\r
211                         | ((uint64_t)~tml) << 0;\r
212 \r
213         // Convert to nanoseconds\r
214         return clocks * 1000 / 24;\r
215 }\r
216 \r
217 void tdma_debug(tdma_t *port)\r
218 {\r
219         int dsr = port->dma->dsr;\r
220         int dcr = port->dma->dcr;\r
221 \r
222         printf("dsr: %s %s %s %s %s %s %d\r\n",\r
223                          dsr & DMA_DSR_BCR_CE_MASK   ? "CE"      : "ce",\r
224                          dsr & DMA_DSR_BCR_BES_MASK  ? "BSE"     : "bse",\r
225                          dsr & DMA_DSR_BCR_BED_MASK  ? "BED"     : "bed",\r
226                          dsr & DMA_DSR_BCR_REQ_MASK  ? "REQ"     : "req",\r
227                          dsr & DMA_DSR_BCR_BSY_MASK  ? "BSY"     : "bsy",\r
228                          dsr & DMA_DSR_BCR_DONE_MASK ? "DONE"    : "done",\r
229                          dsr & DMA_DSR_BCR_BCR_MASK);\r
230 \r
231         printf("dcr: %s %s %s %s %s %s %s %s ssize=%d:%d mod=%d:%d link=%d:%d:%d\r\n",\r
232                          dcr & DMA_DCR_EINT_MASK     ? "EINT"    : "eint",\r
233                          dcr & DMA_DCR_ERQ_MASK      ? "ERQ"     : "erq",\r
234                          dcr & DMA_DCR_CS_MASK       ? "CS"      : "cs",\r
235                          dcr & DMA_DCR_AA_MASK       ? "AA"      : "aa",\r
236                          dcr & DMA_DCR_EADREQ_MASK   ? "EADRREQ" : "eadrreq",\r
237                          dcr & DMA_DCR_SINC_MASK     ? "SINC"    : "sinc",\r
238                          dcr & DMA_DCR_DINC_MASK     ? "DINC"    : "dinc",\r
239                          dcr & DMA_DCR_D_REQ_MASK    ? "DREQ"    : "dreq",\r
240                         (dcr & DMA_DCR_SSIZE_MASK ) >> DMA_DCR_SSIZE_SHIFT,\r
241                         (dcr & DMA_DCR_DSIZE_MASK ) >> DMA_DCR_DSIZE_SHIFT,\r
242                         (dcr & DMA_DCR_SMOD_MASK  ) >> DMA_DCR_SMOD_SHIFT,\r
243                         (dcr & DMA_DCR_DMOD_MASK  ) >> DMA_DCR_DMOD_SHIFT,\r
244                         (dcr & DMA_DCR_LINKCC_MASK) >> DMA_DCR_LINKCC_SHIFT,\r
245                         (dcr & DMA_DCR_LCH1_MASK  ) >> DMA_DCR_LCH1_SHIFT,\r
246                         (dcr & DMA_DCR_LCH2_MASK  ) >> DMA_DCR_LCH2_SHIFT);\r
247 }\r