<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>my tech blog &#187; NXP (Freescale)</title>
	<atom:link href="http://billauer.se/blog/category/freescale/feed/" rel="self" type="application/rss+xml" />
	<link>https://billauer.se/blog</link>
	<description>Anything I found worthy to write down.</description>
	<lastBuildDate>Thu, 12 Mar 2026 11:36:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>NXP / Freescale SDMA and the art of accessing peripheral registers</title>
		<link>https://billauer.se/blog/2017/08/nxp-freescale-sdma-memory-map-throughput/</link>
		<comments>https://billauer.se/blog/2017/08/nxp-freescale-sdma-memory-map-throughput/#comments</comments>
		<pubDate>Thu, 10 Aug 2017 17:58:37 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=5260</guid>
		<description><![CDATA[Preface While writing a custom SDMA script for copying data arriving from an eCSPI peripheral into memory, it occurred to me that there is more than one way to fetch the data from the peripheral. This post summarizes my rather decisive finding in this matter. Spoiler: Linux&#8217; driver could have done better (Freescale&#8217;s v4.1.15) I&#8217;ve [...]]]></description>
			<content:encoded><![CDATA[<h3>Preface</h3>
<p>While writing a custom SDMA script for copying data arriving from an eCSPI peripheral into memory, it occurred to me that there is more than one way to fetch the data from the peripheral. This post summarizes my rather decisive finding in this matter. Spoiler: Linux&#8217; driver could have done better (Freescale&#8217;s v4.1.15)</p>
<p>I&#8217;ve written a <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/" target="_blank">tutorial on SDMA scripts in general</a>, by the way, which is recommended before diving into this one.</p>
<h3>Using the Peripheral DMA Unit</h3>
<p>This is the method used by the official eCSPI driver for Linux. That is, the one obtained from Freescale&#8217;s / NXP&#8217;s Linux git repository. Specifically, spi_imx_sdma_init() in drivers/spi/spi-imx.c sets up the DMA transaction with</p>
<pre>	spi_imx-&gt;rx_config.direction = DMA_DEV_TO_MEM;
	<strong>spi_imx-&gt;rx_config.src_addr = res-&gt;start + MXC_CSPIRXDATA;</strong>
	spi_imx-&gt;rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
	spi_imx-&gt;rx_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
	ret = dmaengine_slave_config(master-&gt;dma_rx, &amp;spi_imx-&gt;rx_config);
	if (ret) {
		dev_err(dev, "error in RX dma configuration.\n");
		goto err;
	}</pre>
<p>Since res-&gt;start points at the address resource obtained from the device tree (0x2008000 for eCSPI1), this is the very same address used for accessing the peripheral registers (only the software uses the virtual address mapped to the relevant region).</p>
<p>In essence, it means issuing an stf command to set the PSA (Peripheral Source Address), and then reading the data with an ldf command on the PD register. For example, if the physical address (e.g. 0x2008000) is in register r1:</p>
<pre>69c3 (0110100111000011) | 	stf	r1, 0xc3	# PSA = r1 for 32-bit frozen periheral read
62c8 (0110001011001000) | 	ldf	r2, 0xc8	# Read peripheral register into r2</pre>
<p>One would expect this to be correct way, or why does this unit exist? Or why does Linux&#8217; driver use it? On the other hand, if this is the right way, why is there a &#8220;DMA mapping&#8221;?</p>
<h3>Using the Burst DMA Unit</h3>
<p>This might sound like a bizarre idea: Use the DMA unit intended for accessing RAM for peripheral registers. I wasn&#8217;t sure this would work at all, but it does: If the same address that was fed into PSA for accessing a peripheral goes into MSA instead, the data can be read correctly from MD. After all, the same address space is used by the processor, Peripheral DMA unit and Burst DMA unit, and it turns out that the buses are interconnected (which isn&#8217;t obvious).</p>
<p>So the example above changes into</p>
<pre>6910 (0110100100010000) | 	stf	r1, 0x10    # To MSA, NO prefetch, address is frozed
620b (0110001000001011) | 	ldf	r2, 0x0b    # Read peripheral register into r2</pre>
<p>The motivation for this type of access is using copy mode &#8212; a burst of up to 8 read/write operations in a single SDMA command. This is possible only from PSA to PDA, or from MSA to MDA. But there is no burst mode from PSA to MDA. So treating the peripheral register as a memory element works around this.</p>
<p>Spoiler: It&#8217;s not such a good idea. The speed results below tell why.</p>
<h3>Using the SDMA internal bus mapping</h3>
<p>The concept is surprisingly simple: It&#8217;s possible to access some peripherals&#8217; registers directly in the SDMA assembly code&#8217;s memory space. In other words, to access eCSPI1, one can go just</p>
<pre>5201 (0101001000000001) | 	ld	r2, (r1, 0) # Read peripheral register from plain SDMA address space</pre>
<p>and achieve the equivalent result of the examples above. But r1 needs to be set to a different address. And this is where it gets a bit confusing.</p>
<p>The base address is fairly easy to obtain. For example,<a href="http://www.nxp.com/docs/en/reference-manual/IMX6SDLRM.pdf" target="_blank"> i.MX6&#8242;s reference manual</a> lists the address for eCSPI1 as 0x2000 in section 2.4 (&#8220;DMA memory map&#8221;), where it also says that the relevant section spans 4 kB. Table 55-14 (&#8220;SDMA Data Memory Space&#8221;) in the same document assigns the region 0x2000-0x2fff to &#8220;per2&#8243;, declares its size as 16 kB, and in the description it says &#8220;peripheral 2 memory space (4 Kbyte peripheral&#8217;s address space)&#8221;. So what is it? 4 kB or 16 kB?</p>
<p>The answer is both: The address 0x2000 is given in SDMA data address format, meaning that each address points at a 32-bit word. Therefore, the SDMA map region of 0x2000-0x2fff indeed spans 16 kB. But the mapping to the peripheral registers was done in a somewhat creative way: The address offsets of the registers apply directly on the SDMA mapping&#8217;s addresses.</p>
<p>For example, let&#8217;s consider the ECSPI1_STATREG, which is placed at &#8220;Base address + 18h offset&#8221;. In the Application Processor&#8217;s address space, it&#8217;s quite clear that it&#8217;s 0x2008000 + 0x18 = 0x2008018. The 0x18 offset means 0x18 (24 in decimal) bytes away from the base.</p>
<p>In the SDMA mapping, the same register is accessed at 0x2000 + 0x18 = 0x2018. At first glance, this might seem obvious, but an 0x18 offset means 24 x 4 = 96 bytes away from the base address. A bit odd, but that&#8217;s the way it&#8217;s implemented.</p>
<p>So even though each address increment in SDMA data address space moves 4 bytes, they mapped the multiply-by-4 offsets directly, placing the registers 16 bytes apart. Attempting to access addresses like 0x2001 yield nothing noteworthy (in my experiments, they all read zero).  I believe that the SDMA submodule was designed in France, by the way.</p>
<p>Almost needless to say, these addresses (e.g. 0x2000) can&#8217;t be used to access peripherals with Peripheral / Burst DMA units &#8212; these units work with the Application Processor&#8217;s bus infrastructure and memory map.</p>
<h3>Speed tests</h3>
<p>As all three methods work, the question is how fast each is. So I ran a speed test.  I only tested the peripheral read operation (my application didn&#8217;t involve writes), but I would expect more or less the same results for writes. The speed tests were carried out by starting the SDMA script from a Linux kernel module,  and issuing a printk when the SDMA script was kicked off. When the interrupt arrived at the completion of the script (resulting from a &#8220;done 3&#8243; opcode, not shown in the table below), another printk was issued. The timestamps in dmeg&#8217;s output was used to measure the time difference.</p>
<p>In order to keep the influence of the Linux overhead delays low, the tested command was executed within a hardware loop, so that the overall execution would take a few seconds. A few milliseconds of printk delay hence became fairly negligible.</p>
<p>The results are given in the following table:</p>
<table border="1" cellspacing="1" cellpadding="1" width="100%">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Peripheral DMA Unit</th>
<th scope="col">Burst DMA Unit</th>
<th scope="col">Internal bus mapping</th>
<th scope="col">Non-IO command</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Assembly code</strong></td>
<td><code> stf    r1, 0xc3<br />
loop endloop, 0<br />
ldf    r2, 0xc8<br />
endloop:<br />
</code></td>
<td><code> stf    r1, 0x10<br />
loop endloop, 0<br />
ldf    r2, 0x0b<br />
endloop:</code></td>
<td><code> loop endloop, 0<br />
ld    r2, (r1, 0)<br />
endloop:</code></td>
<td><code> loop endloop, 0<br />
addi r5, 2<br />
endloop:</code></td>
</tr>
<tr>
<td><strong>Execution rate</strong></td>
<td style="text-align: center;">7.74 Mops/s</td>
<td style="text-align: center;">3.88 Mops/s</td>
<td style="text-align: center;">32.95 Mops/s</td>
<td style="text-align: center;">65.97 Mops/s</td>
</tr>
</tbody>
</table>
<p>Before concluding the results, a word on the rightmost one, which tested the speed of a basic command. The execution rate, almost 66 Mops/s, shows the SDMA machine&#8217;s upper limit. Where this came from isn&#8217;t all that clear, as I couldn&#8217;t find a matching clock rate in any of the three clocks enabled by Linux&#8217; SDMA driver: clk_ahb,  clk_ipg and clk_per.</p>
<p>The reference manual&#8217;s section 55.4.6 claims that the SDMA core&#8217;s frequency is limited to 104 MHz, but calling clk_get_rate() for clk_ahb returned 132 MHz (which is 2 x 66 MHz&#8230;). For the two other which the imx-sdma.c driver declares that it uses, clk_ipg and clk_per (the same clock, I believe), clk_get_rate() returned 60 MHz, so it&#8217;s not that one. In short, it&#8217;s not 100% what&#8217;s going on, except that the figure is max 66 Mops/s.</p>
<p>By the way, I verified that the hardware loop doesn&#8217;t add extra cycles by duplicating the addi command, so it ran10 times for each loop. The execution rate dropped to exactly 1/10, so there&#8217;s definitely no loop overhead.</p>
<p>OK, so now to the conclusions:</p>
<ul>
<li>The clear winner is using the internal bus. Note that the result isn&#8217;t all that impressing, after all. With 33 Mops, 4 bytes each, there&#8217;s a theoretical limit of 132 MB/s for just reading. That doesn&#8217;t include doing something with the data. More about that below.</li>
<li>Note that reading from the internal bus takes just 2 execution cycles.</li>
<li>There is a reason for using the Peripheral DMA Unit, after all: It&#8217;s twice as fast compared with the Burst DMA Unit.</li>
<li>It probably doesn&#8217;t pay off to use the Burst DMA Unit for burst copying from a peripheral to memory, even though I didn&#8217;t give it a go: The read is twice as slow, and writing to memory with autoflush is rather quick (see below).</li>
<li>The use of the Peripheral DMA Unit in the Linux kernel driver is quite questionable, given the results above. On the other hand, the standard set of scripts aren&#8217;t really designed for efficiency anyhow.</li>
</ul>
<h3>Copying data from peripheral to RAM</h3>
<p>In this last pair of speed tests, the loop reads one value from the peripheral with Internal bus mapping (the fastest way found) and writes it to the general RAM with an stf command, using autoincrement. This is hence a realistic scenario for bulk copying of data from a peripheral data register into memory that is available to the Application Processor.</p>
<p>The test code had to be modified slightly, so the destination address is brought back to the beginning of the buffer every 1,000,000 write operations, since the buffer size is limited, quite naturally. So when the script begins, r7 contains the number of times to loop until resetting the destination address (that is, r7 = 1000000) and r3 contains the number of such sessions to run (was set to 200). The overhead of this larger loop is literally one in a million.</p>
<p>The assembly code used was:</p>
<pre>                             | bigloop:
0000 008f (0000000010001111) | 	mov	r0, r7
0001 6e04 (0110111000000100) | 	stf	r6, 0x04	# MDA = r6, incremental write
                             |
0002 7802 (0111100000000010) | 	loop endloop, 0
0003 5201 (0101001000000001) | 	ld	r2, (r1, 0)
0004 6a0b (0110101000001011) | 	stf	r2, 0x0b	# Write 32-bit word, no flush
                             | endloop:
0005 2301 (0010001100000001) | 	subi	r3, 1		# Decrement big loop counter
0006 7cf9 (0111110011111001) | 	bf	bigloop		# Loop until r3 == 0
                             | quit:
0007 0300 (0000001100000000) | 	done 3			# Quit MCU execution</pre>
<p>The result was 20.70 Mops/s, that is 20.7 Million pairs of read-writes per second. This sets the realistic hard upper limit for reading from a peripheral to 82.8 MB/s. Note that deducing the known time it takes to execute the peripheral read, one can estimate that the stf command runs at ~55.5 Mops/s. In other words, it&#8217;s a single cycle instruction until an autoflush is forced every 8 writes. However dropping the peripheral read command (leaving only the stf command) yields only 35.11 Mops/s. So it seems like the DMA burst unit takes advantage of the small pauses between accesses to it.</p>
<p>I should mention that the Linux system was overall idle while performing these tests, so there was little or no congestion on the physical RAM. The results were repeatable within 0.1% of the execution time.</p>
<p>Note that automatic flush was enabled during this test, so the DMA burst unit received 8 writes (32 bytes) before flushing the data into RAM. When reattempting this test, with explicit flush on each write to RAM (exactly the same assembly code as listed above, with a peripheral read and then stf r7, 0x2b instead of 0x0b), the result dropped to 6.83 Mops/s. Which is tantalizingly similar to the 7.74 Mops result obtained for reading from the Peripheral DMA Unit.</p>
<h3>Comparing with non-DMA</h3>
<p>Even though not directly related, it&#8217;s worth comparing how fast the host accesses the same registers. For example, how much time will this take (in Linux kernel code, of course)?</p>
<pre>  for (i=0; i&lt;10000000; i++)
    rc += readl(ecspi_regs + MX51_ECSPI_STAT);</pre>
<p>So the results are as follows:</p>
<ul>
<li>Reading from an eCSPI register (as shown above): 4.10 Mops/s</li>
<li>The same, but from RAM (non-cacheable, allocated with dma_alloc_coherent): 6.93 Mops/s</li>
<li>The same, reading with readl() from a region handled by RAM cache (so it&#8217;s considered volatile): 58.14 Mops/s</li>
<li>Writing to an eCSPI register (with writel(), loop similar to above): 3.8696 Mops/s</li>
</ul>
<p>This was carried out on an i.MX6 processor with a clock frequency of 996 MHz.</p>
<p>The figures echo well with those found in the SDMA tests, so it seems like the dominant delays come from i.MX6&#8242;s bus bridges. It&#8217;s also worth nothing the surprisingly slow performance of readl() from cacheable, maybe because of the memory barriers.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2017/08/nxp-freescale-sdma-memory-map-throughput/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NXP / Freescale i.MX6 as an SPI slave</title>
		<link>https://billauer.se/blog/2017/08/nxp-freescale-spi-slave/</link>
		<comments>https://billauer.se/blog/2017/08/nxp-freescale-spi-slave/#comments</comments>
		<pubDate>Thu, 10 Aug 2017 17:56:58 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=5257</guid>
		<description><![CDATA[Motivation Even though SPI is commonly used for controlling rather low-speed peripherals on an embedded system, it can also come handy for communicating data with an FPGA. When using the official Linux driver, the host can only be the SPI master. It means, among others, that transactions are initiated by the host: When the bursts [...]]]></description>
			<content:encoded><![CDATA[<h3>Motivation</h3>
<p>Even though SPI is commonly used for controlling rather low-speed peripherals on an embedded system, it can also come handy for communicating data with an FPGA.</p>
<p>When using the official Linux driver, the host can only be the SPI master. It means, among others, that transactions are initiated by the host: When the bursts take place is completely decided by software, and so is how long they are. It&#8217;s not just about who drives which lines, but also the fact that the FPGA is on the responding side. This may not be a good solution when the data rates are anything but really slow: If the FPGA is slave, it must wait for the  host to poll it for data (a bit like a USB peripheral). That can become a  bit tricky at the higher end of data rates.</p>
<p>For example, if the FPGA&#8217;s  FIFO is 16 kbit deep, and is filled at 16 Mbit/s, it takes 1 ms for it  to overflow, unless drained by the host. This can be a difficult  real-time task for a user-space Linux program (based upon spidev, for  example). Not to mention how twisted such a solution will end up, having  the processor constantly spinning in a loop collecting data,  whether there is data to collect or not.</p>
<p>Another point is that the SPI clock is always driven by the SPI master, and it&#8217;s usually not a free-running one. Rather, bursts of clock edges are presented on the clock wire to advance the data transaction.</p>
<p>Handling a gated clock correctly on an FPGA isn&#8217;t easy when it&#8217;s controlled by an external device (unless its frequency is quite low). From an FPGA design point of view, it&#8217;s by far simpler to drive the SPI clock and handle the timing of the MOSI/MISO signals with respect to it.</p>
<p>And finally: If a good utilization of the upstream (FPGA to host) SPI channel is desired, putting the FPGA as master has another advantage. For example, on i.MX6 Dual/Quad, the SPI clock cycle  is limited to a cycle of 15 ns for write transactions, but to 40 ns or 55 ns on read transactions, depending on the pins used. The same figures are true, regardless of whether the host is master or slave (compare sections 4.11.2.1and 4.11.2.2 in the relevant datasheet, <a href="http://www.nxp.com/docs/en/data-sheet/IMX6DQCEC.pdf" target="_blank">IMX6DQCEC.pdf</a>). So if the FPGA needs to send data faster than 25 Mbps, it can only use write cycles, hence it has to be the SPI master.</p>
<h3>CS is useless&#8230;</h3>
<p>This is the &#8220;Chip Select&#8221; signal, or &#8220;Slave Select&#8221; (SS) in Freescale / NXP terminology.</p>
<p>The reference manual, along with NXP&#8217;s official errata ERR009535, clearly state that deasserting the SPI&#8217;s CS wire is not a valid way to end a burst. Citing the description for the SS_CTL field of ECSPIx_CONFIGREG, section 21.7.4 in the<a href="http://www.nxp.com/docs/en/reference-manual/IMX6SDLRM.pdf" target="_blank"> i.MX6 Reference Manual</a>:</p>
<blockquote><p>In slave mode &#8211; an SPI burst is completed when the number of bits received in the shift register is equal to (BURST_LENGTH + 1). Only the n least-significant bits (n = BURST_LENGTH[4:0] + 1) of the first received word are valid. All bits subsequent to the first received word in RXFIFO are valid.</p></blockquote>
<p>So the burst length is fixed. The question is, what value to pick. Short answer: 32 bits (set BURST LENGTH to 31).</p>
<p>Why 32? First, let&#8217;s recall that RXFIFO is 32 bits wide. So what is more natural than packing the incoming data into full 32 bits entries in the RXFIFO, fully utilizing its storage capacity? Well, maybe the natural data alignment isn&#8217;t 32 bits, so another packing scheme could have been better. In theory.</p>
<p>That&#8217;s where the second sentence in the citation above comes in. What it effectively says is that if BURST_LENGTH + 1 is chosen anything else than a multiple of 32, the first word, which is <strong>ever</strong> pushed into RXFIFO since the SPI module&#8217;s reset, will contain less than 32 received bits. All the rest, no matter what BURST_LENGTH is set to, will contain 32 bits of received data. This is really what happens. So in the long run, data is packet into 32 bit words no matter what. Choosing BURST_LENGTH + 1 other than a multiple of 32 will just mess up things on the first word the RXFIFO receives after waking up from reset. Nothing else.</p>
<p>So why not set BURST_LENGTH to anything else than 31? Simply because there&#8217;s no reason to do so. We&#8217;re going to end up with an SPI slave that shifts bits into RXFIFO as 32 bit words anyhow. The term &#8220;burst&#8221; has no significance, since deassertions of CS are ignored anyhow. In fact, I&#8217;m not sure if it makes any difference between different values satisfying multiple of 32 rule.</p>
<p>Note that since CS doesn&#8217;t function as a frame for bursts, it&#8217;s important that the eCSPI module is brought out of reset while there&#8217;s no traffic (i.e. clock edges), or it will pack the data in an unaligned and unpredictable manner. Also, if the FPGA accidentally toggles the clock (due to a bug), alignment it lost until the eCSPI is reset and reinitialized.</p>
<p>Bottom line: The SPI slave receiver just counts 32 clock edges, and packs the received data into RXFIFO. Forever. There is no other useful alternative when the host is slave.</p>
<h3>&#8230; but must be taken care of properly</h3>
<p>Since the burst length doesn&#8217;t depend on the CS signal, it might as well be kept asserted all the time. With the register setting given below, that means holding the pin constantly low. It&#8217;s however important to select the correct pin in the CHANNEL_SELECT field of ECSPIx_CONREG: The host will ignore the activity on the SPI bus unless CS is selected. In other words, you can&#8217;t terminate a burst with CS, but if it isn&#8217;t asserted, bits aren&#8217;t sampled.</p>
<p>Another important thing to note, is that the CS pin must be IOMUXed as a CS signal. In the typical device tree for the mainstream Linux SPI master driver, it&#8217;s assigned as a GPIO pin. That&#8217;s no good for an SPI slave.</p>
<p>So, for example, if the ECSPI entry in the device tree says:</p>
<pre>&amp;ecspi1 {
<span style="color: #888888;"><em>[ ... ]</em></span>
	pinctrl-names = "default";
	pinctrl-0 = &lt;&amp;pinctrl_ecspi1_1&gt;;
	status = "okay";
 };</pre>
<p>meaning that the IOMUX settings given in pinctrl_ecspi1_1 should be applied, when the Linux driver related to ecspi1 is probed. It should say something like</p>
<pre>&amp;iomuxc {
	imx6qdl-var-som-mx6 {
<span style="color: #888888;"><em>[ ... ]</em></span>

		pinctrl_ecspi1_1: ecspi1grp {
			fsl,pins = &lt;
				MX6QDL_PAD_DISP0_DAT22__ECSPI1_MISO	0x1f0b1
				MX6QDL_PAD_DISP0_DAT21__ECSPI1_MOSI	0x1f0b1
				MX6QDL_PAD_DISP0_DAT20__ECSPI1_SCLK	0x130b1
				<span style="color: #ff0000;"><strong>MX6QDL_PAD_DISP0_DAT23__ECSPI1_SS0</strong></span>	0x1f0b1
			&gt;;
		};
<em><span style="color: #888888;">[ ... ]</span></em></pre>
<p>The actual labels differ depending on the processor&#8217;s variant, which pins were chosen etc. The point is that the _SS0 usage was selected for the pin, and not the GPIO alternative (in which case it would say <span style="text-decoration: line-through;">MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17</span>). The list of IOMUX defines for the i.MX6 DL variant can be found in arch/arm/boot/dts/imx6dl-pinfunc.h.</p>
<h3>Endianness</h3>
<p>The timing diagrams for SPI communication in the Reference Manual show only 8 bit examples, with  MSB received first. But this applies to 32 bit words as well. But what happens if 4 bytes are sent with the intention of being treated as a string of bytes?</p>
<p>Because the <strong>first</strong> byte is treated as the MSB of a 32-bit word, it&#8217;s going to end up as the <strong>last</strong> byte when the 32-bit word is copied (by virtue of a single 32-bit read and write) into RAM, whether done by the processor or by SDMA. This ensures that a 32-bit integer is interpreted correctly by the Little Endian processor when transmitted over the SPI bus, but messes up single bytes transmitted.</p>
<p>Where exactly this flipping takes place, I&#8221;m not sure, but it doesn&#8217;t really matter. Just be aware that if a sequence of <strong>bytes</strong> are sent over the SPI link, they need to be byte swapped in groups of 4  bytes to appear in the correct order in the processor&#8217;s memory.</p>
<h3>Register setting</h3>
<p>In terms of a Linux kernel driver, the probe of an SPI slave is pretty much the same as the SPI master, with a few obvious differences. For example, the SPI clock&#8217;s frequency isn&#8217;t controlled by the host, so it probably doesn&#8217;t matter so much how the dividers are set (but it&#8217;s probably wise to set these dividers to 1, in case the internal clock is used for something).</p>
<pre>  ctrl = MX51_ECSPI_CTRL_ENABLE | /* Enable module */
    /* MX51_ECSPI_CTRL_MODE_MASK not set, so it's slave mode */
    /* Both clock dividers set to 1 =&gt; 60 MHz, not clear if this matters */
    MX51_ECSPI_CTRL_CS(which_cs) | /* Select CSn */
    (31 &lt;&lt; MX51_ECSPI_CTRL_BL_OFFSET); /* Burst len = 32 bits */

  cfg = 0; /* All defaults, in particular, no clock phase / polarity change */

  /* CTRL register always go first to bring out controller from reset */
  writel(ctrl, regs + MX51_ECSPI_CTRL);

  writel(cfg, regs + MX51_ECSPI_CONFIG);

  /*
   * Wait until the changes in the configuration register CONFIGREG
   * propagate into the hardware. It takes exactly one tick of the
   * SCLK clock, but we will wait 10 us to be sure (SCLK is 60 MHz)
   */

  udelay(10);

  /*
    Turn off DMA requests (revert the register to its defaults)
    But set the RXFIFO watermark as required by device tree.
  */
  writel(MX51_ECSPI_DMA_RX_WML(rx_watermark),
	 regs + MX51_ECSPI_DMA);

  /* Enable interrupt when RXFIFO reaches watermark */
  writel(MX51_ECSPI_INT_RDREN, regs + MX51_ECSPI_INT);</pre>
<p>The example above shows the settings that apply when the the host reads from the RXFIFO directly. Given the measurements I present in <a title="NXP / Freescale SDMA and the art of accessing peripheral registers" href="https://billauer.se/blog/2017/08/nxp-freescale-sdma-memory-map-throughput/" target="_blank">another post of mine</a>, showing ~4 Mops/s with a plain readl() call, it means that at the maximal bus rate of 66 Mbit/s, which is ~2.06 Mops/s (32 bits per read), we have the a processor core 50% busy just on readl() calls.</p>
<p>So for higher data rates, SDMA is pretty much a must.</p>
<h3>The speed test</h3>
<p>Eventually, I ran a test. With a dedicated SDMA script, SPI clock running at 112 MHz, 108.6 Mbit/s actual throughput:</p>
<pre># time dd if=/dev/myspi of=/dev/null bs=64k count=500
500+0 records in
500+0 records out
32768000 bytes (33 MB, 31 MiB) copied, 2.41444 s, 13.6 MB/s

real	0m2.434s
user	0m0.000s
sys	0m1.610s</pre>
<p>This data rate is, of course, way above the allowed SPI clock frequency of 66 MHz, but it&#8217;s not uncommon that real-life results are so much better. I didn&#8217;t bother pushing the clock higher.</p>
<p>I ran a long and rigorous test looking for errors on the data transmission line (~ 1 TB of data) and it was completely clean with the 112 MHz, so the SPI slave is reliable. For a production system, I don&#8217;t think about exceeding 66 MHz, despite this result. Just to have that said.</p>
<p>But the bottom line is that the SPI slave mode can be used as a simple transmission link of 32-bit words. Often that&#8217;s good enough.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2017/08/nxp-freescale-spi-slave/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>i.MX: SDMA not working? Strange things happen? Maybe it&#8217;s all about power management.</title>
		<link>https://billauer.se/blog/2014/07/freescale-imx-linux-dma-uart/</link>
		<comments>https://billauer.se/blog/2014/07/freescale-imx-linux-dma-uart/#comments</comments>
		<pubDate>Sun, 06 Jul 2014 10:06:47 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=3995</guid>
		<description><![CDATA[I ran into a weird problem while attempting to enable SDMA for UARTs on an i.MX53 processor running Freescale&#8217;s 2.6.35.3 Linux kernel: To begin with, the UART would only transmit 48 bytes, which is probably a result of only one watermark event arriving (the initial kickoff filled the UART&#8217;s FIFO with 32 bytes, and then [...]]]></description>
			<content:encoded><![CDATA[<p>I ran into a weird problem while attempting to enable SDMA for UARTs on an i.MX53 processor running Freescale&#8217;s 2.6.35.3 Linux kernel: To begin with, the UART would only transmit 48 bytes, which is probably a result of only one watermark event arriving (the initial kickoff filled the UART&#8217;s FIFO with 32 bytes, and then one SDMA event occurred when the FIFO reached 16 bytes&#8217; fill, so another 16 bytes were sent).</p>
<p>So it seemed like the SDMA core misses the UART&#8217;s watermark events. More scrutinized experiments with my own test scripts revealed a variety of weird behaviors, including what appeared to be preemption of the SDMA script&#8217;s process, even though the reference manual is quite clear about it: Context switching of SDMA scripts is voluntary. And still, the flow of data on the UART&#8217;s tx lines was stopped for 5-6 ms periods randomly, even when I ran a busy-wait loop in the SDMA script, polling the &#8220;not full&#8221; flag of the UART&#8217;s transmission FIFO.</p>
<p>So it looked like something stopped the SDMA script from running in the middle of the loop (which included no &#8220;yield&#8221; nor &#8220;done&#8221; command). Or maybe a completely different issue? Maybe the peripheral bus wasn&#8217;t completely coherent? Anything seemed possible at some point.</p>
<p>As the title implies, the problem was power management, and poor settings of the SDMA&#8217;s behavior during low power modes.</p>
<p>It goes like this: Every time the Linux kernel&#8217;s scheduler has no process to run, it executes an WFI ARM processor command, halting the processor until an interrupt arrives (from a peripheral or just the scheduler&#8217;s tick clock). But before doing that, the kernel calls an architecture-dependent function, arch_idle(), which possibly shuts down or slows down clocks in order to increase power savings.</p>
<p>The kernel I used didn&#8217;t configure the SDMA&#8217;s behavior in the lower-power WAIT mode correctly, causing it halt and miss events while the processor was in this mode. The word is that to overcome this, the CCM_CCGR bits for SDMA clocks should be set to 11 (bits 31-30 in CCM_CCGR4). There is probably also a need to  enable aips_tz1_clk to keep the SDMA and aips_tz1 clocks running. But since the application I worked on didn&#8217;t have any power restrictions, I decided to avoid these power mode switches altogether.</p>
<p>This was done by editing arch/arm/mach-mx5/system.c in the kernel tree, where it said:</p>
<pre>void arch_idle(void)
{
 if (likely(!mxc_jtag_enabled)) {
   if (ddr_clk == NULL)
     ddr_clk = clk_get(NULL, "ddr_clk");
   if (gpc_dvfs_clk == NULL)
     gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
   /* gpc clock is needed for SRPG */
   clk_enable(gpc_dvfs_clk);
   <del><span style="color: #ff0000;"><strong>mxc_cpu_lp_set(arch_idle_mode);</strong></span></del></pre>
<p>and delete the last line in the listing above &#8212; the call to mxc_cpu_lp_set(), which changes the processor&#8217;s power mode.</p>
<p>This solved the SDMA problem for me.</p>
<p>As a matter of fact, I would suggest commenting out this line during the development phase of any i.MX-based system, and return it once everything works. True, this shouldn&#8217;t be an issue if the clocks are properly configured. But if they&#8217;re not, something will fail, and the natural tendency is to focus the drivers of the failing functionality, and not looking for power management issues.</p>
<p>When the power reduction function is re-enabled at some later point, it&#8217;s quite evident what the problem is, if something fails then. So even if the target product is battery-driven, do yourself a favor, and drop that line in system.c until you&#8217;re finished struggling with other things.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2014/07/freescale-imx-linux-dma-uart/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux kernel platform device food chain example</title>
		<link>https://billauer.se/blog/2014/02/linux-kernel-platform/</link>
		<comments>https://billauer.se/blog/2014/02/linux-kernel-platform/#comments</comments>
		<pubDate>Fri, 14 Feb 2014 12:31:03 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=4108</guid>
		<description><![CDATA[Since the device tree is the new way to set up hardware devices on embedded platforms, I hoped that I could avoid the &#8220;platform&#8221; API for picking which driver is going to take control over what. But it looks like the /arch/arm disaster is here to stay for a while, so I need to at [...]]]></description>
			<content:encoded><![CDATA[<p>Since the <a href="http://xillybus.com/tutorials/device-tree-zynq-1" target="_blank">device tree</a> is the new way to set up hardware devices on embedded platforms, I hoped that I could avoid the &#8220;platform&#8221; API for picking which driver is going to take control over what. But it looks like the /arch/arm disaster is here to stay for a while, so I need to at least understand how it works.</p>
<p>So for reference, here&#8217;s an example walkthrough of the SPI driver for i.MX51, declared and matched with a hardware device.</p>
<p>The idea is simple: The driver, which is enabled by .config (and hence the Makefile in its directory includes it for compilation) binds itself to a string during its initialization. On the other side, initialization code requests a device matching that string, and also supplies some information along with that. The example tells the story better.</p>
<p>The platform API is documented in the kernel tree&#8217;s <a href="https://www.kernel.org/doc/Documentation/driver-model/platform.txt" target="_blank">Documentation/driver-model/platform.txt</a>. There&#8217;s also <a href="http://lwn.net/Articles/448499/" target="_blank">a nice LWN article</a> by Jonathan Corbet.</p>
<p>So let&#8217;s assume we have Freescale&#8217;s 3-stack board at hand. in arch/arm/mach-mx5/mx51_3stack.c, at the bottom, it says</p>
<pre>MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board")
 .fixup = fixup_mxc_board,
 .map_io = mx5_map_io,
 .init_irq = mx5_init_irq,
 .init_machine = <strong>mxc_board_init</strong>,
 .timer = &amp;mxc_timer,
MACHINE_EN</pre>
<p>mxc_board_init() is defined in the same file, which among many other calls goes</p>
<pre>mxc_register_device(&amp;mxcspi1_device, &amp;mxcspi1_data);</pre>
<p>with the extra info structure mxcspi1_data defined as</p>
<pre>static struct mxc_spi_master mxcspi1_data = {
 .maxchipselect = 4,
 .spi_version = 23,
 .chipselect_active = mx51_3ds_gpio_spi_chipselect_active,
 .chipselect_inactive = mx51_3ds_gpio_spi_chipselect_inactive,
};</pre>
<p>Now to the declaration of mxcspi1_device: In arch/arm/mach-mx5/devices.c we have</p>
<pre>struct platform_device mxcspi1_device = {
	.name = "<span style="color: #ff0000;"><strong>mxc_spi</strong></span>",
	.id = 0,
	.num_resources = ARRAY_SIZE(<strong>mxcspi1_resources</strong>),
	.resource = mxcspi1_resources,
	.dev = {
		.dma_mask = &amp;spi_dma_mask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	},
};</pre>
<p>and before that, in the same file there was:</p>
<pre>static struct resource <strong>mxcspi1_resources</strong>[] = {
	{
		.start = CSPI1_BASE_ADDR,
		.end = CSPI1_BASE_ADDR + SZ_4K - 1,
		.flags = IORESOURCE_MEM,
	},
	{
		.start = MXC_INT_CSPI1,
		.end = MXC_INT_CSPI1,
		.flags = IORESOURCE_IRQ,
	},
	{
		.start = MXC_DMA_CSPI1_TX,
		.end = MXC_DMA_CSPI1_TX,
		.flags = IORESOURCE_DMA,
	},
};</pre>
<p>So that defines the magic driver string and the resources that are allocated to this device.</p>
<p>It&#8217;s worth noting that devices.c ends with</p>
<pre>postcore_initcall(mxc_init_devices);</pre>
<p>which causes a call to mxc_init_devices(), a function that messes up the addresses of the resources for some architectures. Just to add some confusion. Always watch out for those little traps!</p>
<p>Meanwhile, in drivers/spi/mxc_spi.c</p>
<pre>static struct platform_driver mxc_spi_driver = {
	.driver = {
		   .name = "<span style="color: #ff0000;"><strong>mxc_spi</strong></span>",
		   .owner = THIS_MODULE,
		   },
	.probe = mxc_spi_probe,
	.remove = mxc_spi_remove,
	.suspend = mxc_spi_suspend,
	.resume = mxc_spi_resume,
};</pre>
<p>followed by:</p>
<pre>static int __init mxc_spi_init(void)
{
	pr_debug("Registering the SPI Controller Driver\n");
	return platform_driver_register(&amp;mxc_spi_driver);
}

static void __exit mxc_spi_exit(void)
{
	pr_debug("Unregistering the SPI Controller Driver\n");
	platform_driver_unregister(&amp;mxc_spi_driver);
}

subsys_initcall(mxc_spi_init);
module_exit(mxc_spi_exit);</pre>
<p>So this is how the driver tells Linux that it&#8217;s responsible for devices marked with the &#8220;mxc_spi&#8221; string.</p>
<p>As for some interaction with the device data (also in mxc_spi.c), there&#8217;s stuff like</p>
<pre>mxc_platform_info = (struct mxc_spi_master *)pdev-&gt;dev.platform_data;</pre>
<p>and</p>
<pre>master_drv_data-&gt;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);</pre>
<p>going on with</p>
<pre>if (!request_mem_region(master_drv_data-&gt;res-&gt;start,
			master_drv_data-&gt;res-&gt;end -
			master_drv_data-&gt;res-&gt;start + 1, pdev-&gt;name)) { /* Ayee! */ }</pre>
<p>and</p>
<pre>if (pdev-&gt;dev.dma_mask == NULL) { /* No DMA for you! */ }</pre>
<p>and it goes on&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2014/02/linux-kernel-platform/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cache coherency on i.MX25 running Linux</title>
		<link>https://billauer.se/blog/2014/02/freescale-arm-cache-invalidation-flushing/</link>
		<comments>https://billauer.se/blog/2014/02/freescale-arm-cache-invalidation-flushing/#comments</comments>
		<pubDate>Tue, 04 Feb 2014 14:37:31 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux kernel]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=4084</guid>
		<description><![CDATA[What this blob is all about Running some home-cooked SDMA scripts on Freescale&#8217;s Linux 2.6.28 kernel on an i.MX25 processor, I&#8217;m puzzled by the fact, that cache flushing with dma_map_single(&#8230;, DMA_TO_DEVICE) doesn&#8217;t hurt, but nothing happens if the calls are removed. On the other hand, attempting to remove cache invalidation calls, as in dma_map_single(&#8230;, DMA_FROM_DEVICE) [...]]]></description>
			<content:encoded><![CDATA[<h3>What this blob is all about</h3>
<p>Running some home-cooked SDMA scripts on Freescale&#8217;s Linux 2.6.28 kernel on an i.MX25 processor, I&#8217;m puzzled by the fact, that cache <strong>flushing</strong> with dma_map_single(&#8230;, DMA_TO_DEVICE) doesn&#8217;t hurt, but nothing happens if the calls are removed. On the other hand, attempting to remove cache <strong>invalidation</strong> calls, as in dma_map_single(&#8230;, DMA_FROM_DEVICE) does cause data corruption, as one would expect.</p>
<p>The de-facto lack of need for cache flushing could be explained by the small size of the cache: The sequence of events is typically preparing the data in the buffer, then some stuff in the middle, and only then is the SDMA script kicked off. If the cache lines are evicted naturally as a result of that &#8220;some stuff&#8221; activity, one gets away with not flushing the cache explicitly.</p>
<p>I&#8217;m by no means saying that cache flushing shouldn&#8217;t be done. On the contrary, I&#8217;m surprised that things don&#8217;t break when it&#8217;s removed.</p>
<p>So why doesn&#8217;t one get away with not invalidating the cache? In my tests, I saw 32-byte segments going wrong when I dropped the invalidation. That is, some segments, typically after a handful of successful data transactions of less than 1 kB of data.</p>
<p>Why does dropping the invalidation break things, and dropping the flushing doesn&#8217;t? As I said above, I&#8217;m still puzzled by this.</p>
<p>So I went down to the details of what these calls to dma_map_single() do. Spoiler: I didn&#8217;t find an explanation. At the end of the foodchain, there are several <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0198e/I1014942.html" target="_blank">MCR assembly instructions,</a> as one should expect. Both flushing and invalidation apparently does something useful.</p>
<p>The rest of this post is the dissection of Linux&#8217; kernel code in this respect.</p>
<h3>The gory details</h3>
<p>DMA mappings and sync functions practically wrap the dma_cache_maint() function, e.g. in arch/arm/include/asm/dma-mapping.h:</p>
<pre>static inline dma_addr_t <strong>dma_map_single</strong>(struct device *dev, void *cpu_addr,
		size_t size, enum dma_data_direction dir)
{
	BUG_ON(!valid_dma_direction(dir));

	if (!arch_is_coherent())
		<strong>dma_cache_maint</strong>(cpu_addr, size, dir);

	return virt_to_dma(dev, cpu_addr);
}</pre>
<p>It was verified with disassembly that dma_map_single() was implemented with a call to dma_cache_maint().</p>
<p>This function can be found in arch/arm/mm/dma-mapping.c as follows</p>
<pre>/*
 * Make an area consistent for devices.
 * Note: Drivers should NOT use this function directly, as it will break
 * platforms with CONFIG_DMABOUNCE.
 * Use the driver DMA support - see dma-mapping.h (dma_sync_*)
 */
void <strong>dma_cache_maint</strong>(const void *start, size_t size, int direction)
{
	const void *end = start + size;

	BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(end - 1));

	switch (direction) {
	case DMA_FROM_DEVICE:		/* invalidate only */
		<strong>dmac_inv_range</strong>(start, end);
		outer_inv_range(__pa(start), __pa(end));
		break;
	case DMA_TO_DEVICE:		/* writeback only */
		<strong>dmac_clean_range</strong>(start, end);
		outer_clean_range(__pa(start), __pa(end));
		break;
	case DMA_BIDIRECTIONAL:		/* writeback and invalidate */
		<strong>dmac_flush_range</strong>(start, end);
		outer_flush_range(__pa(start), __pa(end));
		break;
	default:
		BUG();
	}
}
EXPORT_SYMBOL(dma_cache_maint);</pre>
<p>The outer_* calls are defined as null functions in arch/arm/include/asm/cacheflush.h, since the CONFIG_OUTER_CACHE kernel configuration flag isn&#8217;t set.</p>
<p>The dmac_* macros are defined in arch/arm/include/asm/cacheflush.h as follows:</p>
<pre>#define <strong>dmac_inv_range</strong>			__glue(_CACHE,_dma_inv_range)
#define <strong>dmac_clean_range</strong>		__glue(_CACHE,_dma_clean_range)
#define <strong>dmac_flush_range</strong>		__glue(_CACHE,_dma_flush_range)</pre>
<p>where  __glue() simply glues the two strings together (see arch/arm/include/asm/glue.h) and _CACHE equals &#8220;arm926&#8243; for the i.MX25, so e.g. dmac_clean_range becomes arm926_dma_clean_range.<a style="text-decoration: none; border-bottom: 1px dotted #999999; color: #0a0a0a;" href="http://lxr.free-electrons.com/source/arch/arm/include/asm/glue.h"></a></p>
<p>These actual functions are implemented in assembler in arch/arm/mm/proc-arm926.S:</p>
<pre>/*
 *	dma_inv_range(start, end)
 *
 *	Invalidate (discard) the specified virtual address range.
 *	May not write back any entries.  If 'start' or 'end'
 *	are not cache line aligned, those lines must be written
 *	back.
 *
 *	- start	- virtual start address
 *	- end	- virtual end address
 *
 * (same as v4wb)
 */
ENTRY(<strong>arm926_dma_inv_range</strong>)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	tst	r0, #CACHE_DLINESIZE - 1
	mcrne	p15, 0, r0, c7, c10, 1		@ clean D entry
	tst	r1, #CACHE_DLINESIZE - 1
	mcrne	p15, 0, r1, c7, c10, 1		@ clean D entry
#endif
	bic	r0, r0, #CACHE_DLINESIZE - 1
1:	mcr	p15, 0, r0, c7, c6, 1		@ invalidate D entry
	add	r0, r0, #CACHE_DLINESIZE
	cmp	r0, r1
	blo	1b
	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
	mov	pc, lr

/*
 *	dma_clean_range(start, end)
 *
 *	Clean the specified virtual address range.
 *
 *	- start	- virtual start address
 *	- end	- virtual end address
 *
 * (same as v4wb)
 */
ENTRY(<strong>arm926_dma_clean_range</strong>)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	bic	r0, r0, #CACHE_DLINESIZE - 1
1:	mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
	add	r0, r0, #CACHE_DLINESIZE
	cmp	r0, r1
	blo	1b
#endif
	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
	mov	pc, lr

/*
 *	dma_flush_range(start, end)
 *
 *	Clean and invalidate the specified virtual address range.
 *
 *	- start	- virtual start address
 *	- end	- virtual end address
 */
ENTRY(<strong>arm926_dma_flush_range</strong>)
	bic	r0, r0, #CACHE_DLINESIZE - 1
1:
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
#else
	mcr	p15, 0, r0, c7, c6, 1		@ invalidate D entry
#endif
	add	r0, r0, #CACHE_DLINESIZE
	cmp	r0, r1
	blo	1b
	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
	mov	pc, lr</pre>
<p>The CONFIG_CPU_DCACHE_WRITETHROUGH kernel configuration flag is not set, so there are no shortcuts.</p>
<p>Exactly the same snippet, only disassembled from the object file (using objdump -d):</p>
<pre>000004d4 &lt;<strong>arm926_dma_inv_range</strong>&gt;:
 4d4:	e310001f 	tst	r0, #31
 4d8:	1e070f3a 	mcrne	15, 0, r0, cr7, cr10, {1}
 4dc:	e311001f 	tst	r1, #31
 4e0:	1e071f3a 	mcrne	15, 0, r1, cr7, cr10, {1}
 4e4:	e3c0001f 	bic	r0, r0, #31
 4e8:	ee070f36 	mcr	15, 0, r0, cr7, cr6, {1}
 4ec:	e2800020 	add	r0, r0, #32
 4f0:	e1500001 	cmp	r0, r1
 4f4:	3afffffb 	bcc	4e8 &lt;arm926_dma_inv_range+0x14&gt;
 4f8:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 4fc:	e1a0f00e 	mov	pc, lr

00000500 &lt;<strong>arm926_dma_clean_range</strong>&gt;:
 500:	e3c0001f 	bic	r0, r0, #31
 504:	ee070f3a 	mcr	15, 0, r0, cr7, cr10, {1}
 508:	e2800020 	add	r0, r0, #32
 50c:	e1500001 	cmp	r0, r1
 510:	3afffffb 	bcc	504 &lt;arm926_dma_clean_range+0x4&gt;
 514:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 518:	e1a0f00e 	mov	pc, lr

0000051c &lt;<strong>arm926_dma_flush_range</strong>&gt;:
 51c:	e3c0001f 	bic	r0, r0, #31
 520:	ee070f3e 	mcr	15, 0, r0, cr7, cr14, {1}
 524:	e2800020 	add	r0, r0, #32
 528:	e1500001 	cmp	r0, r1
 52c:	3afffffb 	bcc	520 &lt;arm926_dma_flush_range+0x4&gt;
 530:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 534:	e1a0f00e 	mov	pc, lr</pre>
<p>So there&#8217;s actually little to learn from the disassembly. Or at all&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2014/02/freescale-arm-cache-invalidation-flushing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Examples of SDMA-assembler for Freescale i.MX51</title>
		<link>https://billauer.se/blog/2011/11/imx-sdma-assembler-example/</link>
		<comments>https://billauer.se/blog/2011/11/imx-sdma-assembler-example/#comments</comments>
		<pubDate>Sat, 05 Nov 2011 15:13:17 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=2333</guid>
		<description><![CDATA[These are a couple of examples of SDMA assembly code, which performs data copy using the DMA functional unit. The first one shows how to copy data from application memory space to SDMA memory. The second example copies data from one application memory chunk to another, and hence works as an offload memcpy(). To actually [...]]]></description>
			<content:encoded><![CDATA[<p>These are a couple of examples of SDMA assembly code, which performs data copy using the DMA functional unit. The first one shows how to copy data from application memory space to SDMA memory. The second example copies data from one application memory chunk to another, and hence works as an offload memcpy().</p>
<p>To actually use this code and generally understand what&#8217;s going on here, I&#8217;d warmly suggest reading <a title="Freescale i.MX51 SDMA tutorial (part IV)" href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">a previous post of mine</a> about SDMA assembly code, which also explains how to compile the code and gives the context for the C functions given below.</p>
<h3>Gotchas</h3>
<ul>
<li>Never let either the source address nor the destination address cross a 32-byte boundary during a burst from or to the internal FIFO. Even though I haven&#8217;t seen this restriction in the official documentation, several unexplained misbehaviors have surfaces when allowing this happen, in particular when accessing EIM. So just don&#8217;t.</li>
<li>When accessing EIM, the EIM&#8217;s maximal burst length must be set to allow 32 bytes in one burst with the BL parameter, or data gets corrupted.</li>
</ul>
<h3>Application space memory to SDMA space</h3>
<p>The assembly code goes</p>
<pre>$ ./sdma_asm.pl app2sdma.asm
 | # Always in context (not altered by script):
 | #
 | # r4 : Physical address to source in AP memory space
 | # r6 : Address in SDMA space to copy to
 | # r7 : Number of DWs to copy   
 | #
 | # Both r4 and r5 must be DW aligned.
 | # Note that prefetching is allowed, so up to 8 useless DWs may be read.
 |
 | # First, load the status registers into SDMA space
                             | start:
0000 6c20 (0110110000100000) |     stf r4, 0x20 # To MSA, prefetch on, address is nonfrozen
0001 008f (0000000010001111) |     mov r0, r7
0002 018e (0000000110001110) |     mov r1, r6
0003 7803 (0111100000000011) |     loop postloop, 0
0004 622b (0110001000101011) |     ldf r2, 0x2b # Read from 32 bits from MD with prefetch
0005 5a01 (0101101000000001) |     st r2, (r1, 0) # Address in r1
0006 1901 (0001100100000001) |     addi r1, 1
                             | postloop:
0007 0300 (0000001100000000) |     done 3
0008 0b00 (0000101100000000) |     ldi r3, 0
0009 4b00 (0100101100000000) |     cmpeqi r3, 0 # Always true
000a 7df5 (0111110111110101) |     bt start # Always branches

------------ CUT HERE -----------

static const int sdma_code_length = 6;
static const u32 sdma_code[6] = {
 0x6c20008f, 0x018e7803, 0x622b5a01, 0x19010300, 0x0b004b00, 0x7df50000,
};</pre>
<p>Note that the arguments for sdf and ldf are given as numbers, and not following the not-so-helpful notation used in the Reference Manual.</p>
<p>The basic idea behind the assembly code is that each DW (Double Word, 32 bits) is read automatically by the functional unit from application space memory, and then fetched from the FIFO into r2. Then the register is written to SDMA memory with a plain &#8220;st&#8221; opcode.</p>
<p>The relevant tryrun() function to test this is:</p>
<pre>static int tryrun(struct sdma_engine *sdma)
{
 dma_addr_t src_phys;
 void *src_virt;

 const int channel = 1;
 struct sdma_channel *sdmac = &amp;sdma-&gt;channel[channel];
 static const u32 sdma_code[6] = {
   0x6c20008f, 0x018e7803, 0x622b5a01, 0x19010300, 0x0b004b00, 0x7df50000,
 };

 static const u32 sample_data[8] = {
   0x12345678, 0x11223344, 0xdeadbeef, 0xbabecafe,
   0xebeb0000, 0, 0xffffffff, 0xabcdef00 };

 const int origin = 0xe00; // In data space terms (32 bits/address)

 struct sdma_context_data *context = sdma-&gt;context;

 int ret;

 src_virt = dma_alloc_coherent(NULL,
                               4096, // 4096 bytes, just any buffer size
                               &amp;src_phys, GFP_KERNEL);
 if (!src_virt) {
   printk(KERN_ERR "Failed to allocate source buffer memory\n");
   return -ENOMEM;
 }

 memset(src_virt, 0, 4096);

 memcpy(src_virt, sample_data, sizeof(sample_data));

 sdma_write_datamem(sdma, (void *) sdma_code, sizeof(sdma_code), origin);

 ret = sdma_request_channel(sdmac);

 if (ret) {
   printk(KERN_ERR "Failed to request channel\n");
   return ret;
 }

 sdma_disable_channel(sdmac);
 sdma_config_ownership(sdmac, false, true, false);

 memset(context, 0, sizeof(*context));

 context-&gt;channel_state.pc = origin * 2; // In program space addressing...
 context-&gt;gReg[4] = src_phys;
 context-&gt;gReg[6] = 0xe80;
 context-&gt;gReg[7] = 3; // Number of DWs to copy

 ret = sdma_write_datamem(sdma, (void *) context, sizeof(*context),
 0x800 + (sizeof(*context) / 4) * channel);

 if (ret) {
   printk(KERN_ERR "Failed to load context\n");
   return ret;
 }

 ret = sdma_run_channel(&amp;sdma-&gt;channel[1]);

 sdma_print_mem(sdma, 0xe80, 128);

 if (ret) {
   printk(KERN_ERR "Failed to run script!\n");
   return ret;
 }

 return 0; /* Success! */
}</pre>
<p>Note that the C code snippet, which is part of the output of the assembler compilation, actually appears in the tryrun() function.</p>
<h3>Fast memcpy()</h3>
<p>Assembly goes</p>
<pre>$ ./sdma_asm.pl copydma.asm
 | # Should be set up at invocation
 | #
 | # r0 : Number of DWs to copy (is altered as script runs)
 | # r1 : Source address (DW aligned)
 | # r2 : Destination address (DW aligned)
 |
0000 6920 (0110100100100000) |     stf r1, 0x20 # To MSA, prefetch on, address is nonfrozen
0001 6a04 (0110101000000100) |     stf r2, 0x04 # To MDA, address is nonfrozen
0002 0c08 (0000110000001000) |     ldi r4, 8 # Number of DWs to copy each round
                             | copyloop:
0003 04d8 (0000010011011000) |     cmphs r4, r0 # Is 8 larger or equal to the number of DWs left to copy?
0004 7d03 (0111110100000011) |     bt lastcopy  # If so, jump to last transfer label
0005 6c18 (0110110000011000) |     stf r4, 0x18 # Copy 8 words from MSA to MDA address.
0006 2008 (0010000000001000) |     subi r0, 8   # Decrement counter
0007 7cfb (0111110011111011) |     bf copyloop  # Always branches, because r0 &gt; 0
                             | lastcopy:
0008 6818 (0110100000011000) |     stf r0, 0x18 # Copy 8 or less DWs (r0 is always &gt; 0)
                             | exit:
0009 0300 (0000001100000000) |     done 3
000a 0b00 (0000101100000000) |     ldi r3, 0
000b 4b00 (0100101100000000) |     cmpeqi r3, 0 # Always true
000c 7dfc (0111110111111100) |     bt exit # Endless loop, just to be safe

------------ CUT HERE -----------

static const int sdma_code_length = 7;
static const u32 sdma_code[7] = {
 0x69206a04, 0x0c0804d8, 0x7d036c18, 0x20087cfb, 0x68180300, 0x0b004b00, 0x7dfc0000,
}</pre>
<p>For a frozen (constant) source address (e.g. when reading from a FIFO) the first stf should be done with argument 0x30 rather than 0x20. For a frozen destination address, the seconds stf has the argument 0x14 instead of 0x04.</p>
<p>This script should be started with r0 &gt; 0. It may be OK to have r0=0, but I&#8217;m not sure about that (and if there&#8217;s no issue with not reading any data after a prefetch, as possibly related to section 52.22.1 in the Reference Manual).</p>
<p>The endless loop to &#8220;exit&#8221; should never be needed. It&#8217;s there just in case the script is rerun by mistake, so it responds with a &#8220;done&#8221; right away. And the example above is not really optimal: To make a for-sure branch, I could have gone &#8220;bt exit&#8221; and &#8220;bf exit&#8221; immediately after it, making this in two opcodes instead of three. Wasteful me.</p>
<p>The tryrun() function for this case then goes</p>
<pre>static int tryrun(struct sdma_engine *sdma)
{
 dma_addr_t buf_phys;
 u8 *buf_virt;

 const int channel = 1;
 struct sdma_channel *sdmac = &amp;sdma-&gt;channel[channel];

 static const u32 sdma_code[7] = {
   0x69206a04, 0x0c0804d8, 0x7d036c18, 0x20087cfb, 0x68180300, 0x0b004b00, 0x7dfc0000,
 };

 static const u32 sample_data[8] = {
                                    0x12345678, 0x11223344, 0xdeadbeef, 0xbabecafe,
                                    0xebeb0000, 0, 0xffffffff, 0xabcdef00 };

 const int origin = 0xe00; // In data space terms (32 bits/address)

 struct sdma_context_data *context = sdma-&gt;context;

 int ret;

 buf_virt = dma_alloc_coherent(NULL, 4096,
                               &amp;buf_phys, GFP_KERNEL);
 if (!buf_virt) {
   printk(KERN_ERR "Failed to allocate source buffer memory\n");
   return -ENOMEM;
 }

 memset(buf_virt, 0, 4096);

 memcpy(buf_virt, sample_data, sizeof(sample_data));

 sdma_write_datamem(sdma, (void *) sdma_code, sizeof(sdma_code), origin);

 ret = sdma_request_channel(sdmac);

 if (ret) {
   printk(KERN_ERR "Failed to request channel\n");
   return ret;
 }

 sdma_disable_channel(sdmac);
 sdma_config_ownership(sdmac, false, true, false);

 memset(context, 0, sizeof(*context));

 context-&gt;channel_state.pc = origin * 2; // In program space addressing...
 context-&gt;gReg[0] = 18; // Number of DWs to copy
 context-&gt;gReg[1] = buf_phys;
 context-&gt;gReg[2] = buf_phys + 0x40;

 ret = sdma_write_datamem(sdma, (void *) context, sizeof(*context),
                          0x800 + (sizeof(*context) / 4) * channel);

 if (ret) {
   printk(KERN_ERR "Failed to load context\n");
   return ret;
 }

 ret = sdma_run_channel(&amp;sdma-&gt;channel[1]);

do {
 int i;
 const int len = 0xa0;

 unsigned char line[128];
 int pos = 0;

 for (i=0; i&lt;len; i++) {
   if ((i % 16) == 0)
   pos = sprintf(line, "%04x ", i);

   pos += sprintf(&amp;line[pos], "%02x ", buf_virt[i]);

   if ((i % 16) == 15)
     printk(KERN_WARNING "%s\n", line);
   }
 } while (0);

 if (ret) {
   printk(KERN_ERR "Failed to run script!\n");
   return ret;
 }

 return 0; /* Success! */
}</pre>
<p>The memory&#8217;s content  is printed out here from tryrun() directly, since the dumped memory is in application space.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2011/11/imx-sdma-assembler-example/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Freescale i.MX SDMA tutorial (part IV)</title>
		<link>https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/</link>
		<comments>https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/#comments</comments>
		<pubDate>Wed, 26 Oct 2011 17:57:36 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=2237</guid>
		<description><![CDATA[This is part IV of a brief tutorial about the i.MX51&#8242;s SDMA core. The SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly the same, with changes in the registers&#8217; addresses and different chapters in the Reference Manual. This is by no means a replacement for reading the Reference Manual, but rather [...]]]></description>
			<content:encoded><![CDATA[<p>This is part IV of a brief tutorial about the i.MX51&#8242;s SDMA core. The  SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly  the same, with changes in the registers&#8217; addresses and different  chapters in the Reference Manual.</p>
<p>This is by no means a replacement for reading the Reference Manual, but rather an introduction to make the landing softer. The division into part goes as follows:</p>
<ul>
<li>Part I: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/" target="_blank">Introduction, addressing and the memory map</a></li>
<li>Part II: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/" target="_blank">Contexts, Channels, Scripts and their execution</a></li>
<li>Part III: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/" target="_blank">Events and Interrupts</a></li>
<li>Part IV: Running custom SDMA scripts in Linux (this page)</li>
</ul>
<h3>Running custom scripts</h3>
<p>I&#8217;ll try to show the basics of getting a simple custom script to run on the SDMA core. Since there&#8217;s a lot of supporting infrastructure involved, I&#8217;ll show my example as a hack on the drivers/dma/imx-sdma.c Linux kernel module per version 2.6.38. I&#8217;m not going to explain the details of kernel hacking, so without experience in that field, it will be pretty difficult to try this out yourself.</p>
<p>The process of running an application-driven custom script consists of the following steps:</p>
<ul>
<li>Initialize the SDMA module</li>
<li>Initialize the SDMA channel and clearing its HE flag</li>
<li>Copy the SDMA assembly code from application space memory to SDMA memory space RAM.</li>
<li>Set up the channel&#8217;s context</li>
<li>Enable the channel&#8217;s HE flag (so the script runs pretty soon)</li>
<li>Wait for interrupt (assuming that the script ends with a &#8220;DONE 3&#8243;)</li>
<li>Possibly copy back the context to application processor space, to inspect the registers upon termination, and verify that their values are as expected.</li>
<li>Possibly copy SDMA memory to application processor space in order to inspect if the script worked as expected (if the script writes to SDMA RAM)</li>
</ul>
<p>The first two steps are handled by the imx-smda.c kernel module, so I won&#8217;t cover them. I&#8217;ll start with the assembly code, which has to be generated first.</p>
<h3>The assembler</h3>
<p>Freescale offers their assembler, but I decided to write my own in Perl. It&#8217;s simple and useful for writing short routines, and its output is snippets of C code, which can be inserted directly into the source, as I&#8217;ll show later. It&#8217;s released under GPLv2, and you can download it from <a href="https://billauer.se/download/sdma_asm.tar.gz" target="_blank">this link</a>.</p>
<p>The sample code below does nothing useful. For a couple of memory related examples, please see <a title="Examples of SDMA-assembler for Freescale i.MX51" href="https://billauer.se/blog/2011/11/imx-sdma-assembler-example/" target="_blank">another post of mine</a>.</p>
<p>To try it out quickly, just untar it on some UNIX system (Linux included, of course), change directory to sdma_asm, and go</p>
<pre>$ ./sdma_asm.pl looptry.asm
                             | start:
0000 0804 (0000100000000100) |     ldi r0, 4
0001 7803 (0111100000000011) |     loop exit, 0
0002 5c05 (0101110000000101) |     st r4, (r5, 0) # Address r5
0003 1d01 (0001110100000001) |     addi r5, 1
0004 1c10 (0001110000010000) |     addi r4, 0x10
                             | exit:
0005 0300 (0000001100000000) |     done 3
0006 1c40 (0001110001000000) |     addi r4, 0x40
0007 0b00 (0000101100000000) |     ldi r3, 0
0008 4b00 (0100101100000000) |     cmpeqi r3, 0 # Always true
0009 7df6 (0111110111110110) |     bt start # Always branches

------------ CUT HERE -----------

static const int sdma_code_length = 5;
static const u32 sdma_code[5] = {
 0x08047803, 0x5c051d01, 0x1c100300, 0x1c400b00, 0x4b007df6,
};</pre>
<p>The output should be pretty obvious. In particular, note that there&#8217;s a C declaration of a const array called sdma_code, which I&#8217;ll show how to use below. The first part of the output is a plain assembly listing, with the address, hex code and binary representation of the opcodes. There are a few simple syntax rules to observe:</p>
<ul>
<li>Anything after a ‘;’ or ‘#’ sign is ignored (comments)</li>
<li>Empty lines are ignored, of course</li>
<li>A label starts the line, and is followed by a colon sign, &#8216;:&#8217;</li>
<li>Everything is case-insensitive, including labels (all code is lowercased internally)</li>
<li>The first alphanumeric string is considered the opcode, unless it&#8217;s a label</li>
<li>Everything following an opcode (comments excluded) is considered the arguments</li>
<li>All registers are noted as r0, r1, … r7 in the argument fields, and  not as plain numbers, unlike the way shown in the reference manual. This  makes a clear distinction between registers and values. It’s “st <strong>r</strong>7, (<strong>r</strong>0,9)” and not “<del>st 7, (0,9)</del>“.</li>
<li>Immediate arguments can be represented as decimal numbers (digits  only), possibly negative (with a plain ‘-’ prefix). Positive hexadecimal  numbers are allowed with the classic C “0x” prefix.</li>
<li>Labels are allowed for loops, as the first argument. The label is understood to be the first statement <strong>after</strong> the loop, so the label is the point reached when the loop is <strong>finished</strong>. See the example above. The second argument may not be omitted.</li>
<li>Other than loops, labels are accepted only for branch instructions, where the jump is  relative. Absolute jump addresses can’t be generated automatically for  jmp and jsr because the absolute address is not known during assembly.</li>
</ul>
<p>A few words about why labels are not allowed for absolute jumps: It would be pretty simple to tell the Perl script the origin address, and allow absolute addressed jumps. I believe absolute jumps within a custom script should be avoided at any cost, so that the object code can be stored and run anywhere vacant. This is why I wasn&#8217;t keen on implementing this.</p>
<h3>A simple test function</h3>
<p>This is a simple function, which loads a custom script and runs it a few times. I added it, and a few additional functions (detailed later) to the Linux kernel&#8217;s SDMA driver, imx-sdma.c, and called it at the end of sdma_probe(). This is the simplest, yet not most efficient way to try things out: The operation takes place once when the module is inserted into the kernel, and then a reboot is necessary, since the module can&#8217;t be removed from the kernel. But with the reboot being fairly quick on an embedded system, it&#8217;s pretty OK.</p>
<p>So here&#8217;s the tryrun() function. Mind you, it&#8217;s called after the SDMA subsystem has been initialized, with one argument, the pointer to the sdma_engine structure (there&#8217;s only one for the entire system).</p>
<pre>static int tryrun(struct sdma_engine *sdma)
{
 const int channel = 1;
 struct sdma_channel *sdmac = &amp;sdma-&gt;channel[channel];
 static const u32 sdma_code[5] = {
  0x08047803, 0x5c051d01, 0x1c100300, 0x1c400b00, 0x4b007df6,
 };

 const int origin = 0xe00; /* In data space terms (32 bits/address) */

 struct sdma_context_data *context = sdma-&gt;context;

 int ret;
 int i;

 sdma_write_datamem(sdma, (void *) sdma_code, sizeof(sdma_code), origin);

 ret = sdma_request_channel(sdmac);

 if (ret) {
   printk(KERN_ERR "Failed to request channel\n");
   return ret;
 }

 sdma_disable_channel(sdmac);
 sdma_config_ownership(sdmac, false, true, false);

 memset(context, 0, sizeof(*context));

 context-&gt;channel_state.pc = origin * 2; /* In program space addressing... */
 context-&gt;gReg[4] = 0x12345678;
 context-&gt;gReg[5] = 0xe80;

 ret = sdma_write_datamem(sdma, (void *) context, sizeof(*context),
                          0x800 + (sizeof(*context) / 4) * channel);
 if (ret) {
   printk(KERN_ERR "Failed to load context\n");
   return ret;
 }

 for (i=0; i&lt;4; i++) {
   ret = sdma_run_channel(&amp;sdma-&gt;channel[1]);
   printk(KERN_WARNING "*****************************\n");
   sdma_print_mem(sdma, 0xe80, 128);

   if (ret) {
     printk(KERN_ERR "Failed to run script!\n");
     return ret;
   }
 }
 return 0; /* Success! */
}</pre>
<h3>Copying the code into SDMA memory</h3>
<p>First, note that sdma_code is indeed copied from the output of the assembler, when it&#8217;s executed on looptry.asm as shown above. The assembler adds the &#8220;static&#8221; modifier as well as an sdma_code_length variable which were omitted, but otherwise it&#8217;s an exact copy.</p>
<p>The first thing the function actually does, is calling sdma_write_datamem() to copy the code into SDMA space (and I don&#8217;t check the return value, sloppy me). This is a function I&#8217;ve added, but its clearly derived from sdma_load_context(), which is part of imx-sdma.c:</p>
<pre>static int sdma_write_datamem(struct sdma_engine *sdma, void *buf,
                              int size, u32 address)
{
 struct sdma_buffer_descriptor *bd0 = sdma-&gt;channel[0].bd;
 void *buf_virt;
 dma_addr_t buf_phys;
 int ret;

 buf_virt = dma_alloc_coherent(NULL, size, &amp;buf_phys, GFP_KERNEL);
 if (!buf_virt)
 return -ENOMEM;

 bd0-&gt;mode.command = C0_SETDM;
 bd0-&gt;mode.count = size / 4;
 bd0-&gt;mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
 bd0-&gt;buffer_addr = buf_phys;
 bd0-&gt;ext_buffer_addr = address;

 memcpy(buf_virt, buf, size);

 ret = sdma_run_channel(&amp;sdma-&gt;channel[0]);

 dma_free_coherent(NULL, size, buf_virt, buf_phys);

 return ret;
}</pre>
<p>The sdma_write_datamem()&#8217;s principle of operation is pretty simple: First a buffer is allocated, with its address in virtual space given in buf_virt and its physical address is buf_phys. Both addresses are related to the application processor, of course.</p>
<p>Then the buffer descriptor is set up. This piece of memory is preallocated globally for the entire sdma engine (in application processor&#8217;s memory space), which isn&#8217;t the cleanest way to do it, but since these operations aren&#8217;t expected to happen in parallel processes, this is OK. The sdma_buffer_descriptor structure is defined in imx-smda.c itself, and is initialized according to section 52.23.1 in the Reference Manual. Note that this calling convention interfaces with the script running on channel 0, and not with any hardware interface. This chunk is merely telling the script what to do. In particular, the C0_SETDM command tells it to copy from application memory space to SDMA data memory space (see section 53.23.1.2).</p>
<p>Note that in the function&#8217;s arguments, &#8220;size&#8221; is given in bytes, but address in SDMA data address space (that is, in 32-bit quanta). This is why &#8220;size&#8221; is divided by four to become the element count (mode.count).</p>
<p>Just before kicking off, the input buffer&#8217;s data is copied into the dedicated buffer with a plain memcpy() command.</p>
<p>And then sdma_run_channel() (part of imx-sdma.c) is called to make channel 0 runnable. This function merely sets the HE bit of channel 0, and waits (sleeping) for the interrupt to arrive, or errors on timeout after a second.</p>
<p>At this point we have the script loaded into SDMA RAM (at data address 0xe00).</p>
<h3>Some housekeeping calls on channel 1</h3>
<p>Up to this point, nothing was done on the channel we&#8217;re going to use, which is channel #1. Three calls to functions defined in imx-sdma.c prepare the channel for use:</p>
<ul>
<li>sdma_request_channel() sets up the channel&#8217;s buffer descriptor and data structure, and enables the clock global to the entire sdma engine, actions which I&#8217;m not sure are necessary. It also sets up the channel&#8217;s priority and the Linux&#8217; wait queue (used when waiting for interrupt).</li>
<li>sdma_disable_channel() clears the channel&#8217;s HE flag</li>
<li>sdma_config_ownership() clears HO, sets EO and DO for the channel, so the channel is driven (&#8220;owned&#8221;) by the processor (as opposed to driven by external events).</li>
</ul>
<h3>Setting up the context</h3>
<p>Even though imx-sdma.c has a sdma_load_context() function, it&#8217;s written for setting up the context as suitable for running the channel 0 script. To keep things simpler, we&#8217;ll set up the context directly.</p>
<p>After zeroing the entire structure, three registers are set in tryrun(): The program counter, r4 and r5. Note that the program counter is given the address to which the code was copied, <strong>multiplied by 2</strong>, since the program counter is given in program memory space. The two other registers are set merely as an initial state for the script. The structure is then copied into the per-channel designated slot with sdma_write_datamem().</p>
<p>Again, note that the &#8220;context&#8221; data structure, which is used as a source buffer from which the context is copied into SDMA memory, is allocated globally for the entire SDMA engine. It&#8217;s not even protected by a mutex, so in a real project you should allocate your own piece of memory to hold the sdma_context structure.</p>
<h3>Running the script</h3>
<p>In the end, we have a loop of four subsequent runs of the script, without updating the context, so from the second time and on, the script continues after the &#8220;done 3&#8243; instruction. This is possible, because the script jumps to the beginning upon resumption (the three last lines in the assembly code, see above).</p>
<p>Each call to sdma_run_channel() sets channel 1&#8242;s HE flag, making it do its thing and then trigger off an interrupt with the DONE instruction, which in turn wakes up the process telling it the script has finished. sdma_print_mem() merely makes a series of printk&#8217;s, consisting of hex dumps of data from the SDMA memory. As used, it&#8217;s aimed on the region which the script is expected to alter, but the same function can be used to verify that the script is indeed in its place, or look at the memory. The function  goes</p>
<pre>static int sdma_print_mem(struct sdma_engine *sdma, int start, int len)
{
 int i;
 u8 *buf;
 unsigned char line[128];
 int pos = 0;

 len = (len + 15) &amp; 0xfff0;

 buf = kzalloc(len, GFP_KERNEL);

 if (!buf)
   return -ENOMEM;

 sdma_fetch_datamem(sdma, buf, len, start);

 for (i=0; i&lt;len; i++) {
   if ((i % 16) == 0)
     pos = sprintf(line, "%04x ", i);

   pos += sprintf(&amp;line[pos], "%02x ", buf[i]);

   if ((i % 16) == 15)
     printk(KERN_WARNING "%s\n", line);
 }

 kfree(buf);

 return 0;
}</pre>
<p>and it uses this function (note that the instruction is C0_GETDM):</p>
<pre>static int sdma_fetch_datamem(struct sdma_engine *sdma, void *buf,
                              int size, u32 address)
{
 struct sdma_buffer_descriptor *bd0 = sdma-&gt;channel[0].bd;
 void *buf_virt;
 dma_addr_t buf_phys;
 int ret;

 buf_virt = dma_alloc_coherent(NULL, size,
                               &amp;buf_phys, GFP_KERNEL);
 if (!buf_virt)
   return -ENOMEM;

 bd0-&gt;mode.command = C0_GETDM;
 bd0-&gt;mode.count = size / 4;
 bd0-&gt;mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
 bd0-&gt;buffer_addr = buf_phys;
 bd0-&gt;ext_buffer_addr = address;

 ret = sdma_run_channel(&amp;sdma-&gt;channel[0]);

 memcpy(buf, buf_virt, size);

 dma_free_coherent(NULL, size, buf_virt, buf_phys);

 return ret;
}</pre>
<h3>Dumping context</h3>
<p>This is the poor man&#8217;s debugger, but it&#8217;s pretty useful. A &#8220;done 3&#8243; function can be seen as a breakpoint, and the context dumped to the kernel log with this function:</p>
<pre>static int sdma_print_context(struct sdma_engine *sdma, int channel)
{
 int i;
 struct sdma_context_data *context;
 u32 *reg;
 unsigned char line[128];
 int pos = 0;
 int start = 0x800 + (sizeof(*context) / 4) * channel;
 int len = sizeof(*context);
 const char *regnames[22] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
                              "mda", "msa", "ms", "md",
                              "pda", "psa", "ps", "pd",
                              "ca", "cs", "dda", "dsa", "ds", "dd" };

 context = kzalloc(len, GFP_KERNEL);

 if (!context)
   return -ENOMEM;

 sdma_fetch_datamem(sdma, context, len, start);

 printk(KERN_WARNING "pc=%04x rpc=%04x spc=%04x epc=%04x\n",
   context-&gt;channel_state.pc,
   context-&gt;channel_state.rpc,
   context-&gt;channel_state.spc,
   context-&gt;channel_state.epc
 );

 printk(KERN_WARNING "Flags: t=%d sf=%d df=%d lm=%d\n",
   context-&gt;channel_state.t,
   context-&gt;channel_state.sf,
   context-&gt;channel_state.df,
   context-&gt;channel_state.lm
 );       

 reg = &amp;context-&gt;gReg[0];

 for (i=0; i&lt;22; i++) {
   if ((i % 4) == 0)
     pos = 0;

   pos += sprintf(&amp;line[pos], "%s=%08x ", regnames[i], *reg++);

   if (((i % 4) == 3) || (i == 21))
     printk(KERN_WARNING "%s\n", line);
 }

 kfree(context);

 return 0;
}</pre>
<h3>Clashes with Linux&#8217; SDMA driver</h3>
<p>Playing around with the SDMA subsystem directly is inherently problematic, since the assigned driver may take contradicting actions, possibly leading to a system lockup. Running custom scripts using the existing driver isn&#8217;t possible, since it has no support for that as of kernel 2.6.38. On the other hand, there&#8217;s a good chance that the SDMA driver wasn&#8217;t enabled at all when the kernel was compiled, in which case there is no chance for collisions.</p>
<p>The simplest way to verify if the SDMA driver is currently present in the kernel, is to check in /proc/interrupts whether interrupt #6 is taken (it&#8217;s the SDMA interrupt).</p>
<p>The &#8220;imx-sdma&#8221; pseudodevice is always registered on the platfrom pseudobus (I suppose that will remain in the transition to Open Firmware), no matter the configuration. It&#8217;s the driver which may not be present. The &#8220;i.MX SDMA support&#8221; kernel option (CONFIG_IMX_SDMA) may not be enabled (it can be a module). Note that it depends on the general &#8220;DMA Engine Support&#8221; (CONFIG_DMADEVICES), which may not be enabled to begin with.</p>
<p>Anyhow, for playing with the SDMA module, it&#8217;s actually better when these are not enabled. In the long run, maybe there&#8217;s a need to expand imx-sdma.c, so it supports custom SDMA scripting. The question remaining is to what extent it should manage the SDMA RAM. Well, the real question is if there&#8217;s enough community interest in custom SDMA scripting at all.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Freescale i.MX51 SDMA tutorial (part III)</title>
		<link>https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/</link>
		<comments>https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/#comments</comments>
		<pubDate>Wed, 26 Oct 2011 15:35:46 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=2224</guid>
		<description><![CDATA[This is part III of a brief tutorial about the i.MX51&#8242;s SDMA core. The SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly the same, with changes in the registers&#8217; addresses and different chapters in the Reference Manual. This is by no means a replacement for reading the Reference Manual, but rather [...]]]></description>
			<content:encoded><![CDATA[<p>This is part III of a brief tutorial about the i.MX51&#8242;s SDMA core. The   SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly   the same, with changes in the registers&#8217; addresses and different   chapters in the Reference Manual.</p>
<p>This is by no means a  replacement for reading the Reference Manual, but rather an introduction  to make the landing softer. The division into part goes as follows:</p>
<ul>
<li>Part I: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/" target="_blank">Introduction, addressing and the memory map</a></li>
<li>Part II: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/" target="_blank">Contexts, Channels, Scripts and their execution</a></li>
<li>Part III: Events and Interrupts (this page)</li>
<li>Part IV: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">Running custom SDMA scripts in Linux</a></li>
</ul>
<h3>Events</h3>
<p>Even though an SDMA script can be kicked off (or made eligible for running, to be precise) by the application processor, regardless of any external events, there&#8217;s a lot of sense in letting the peripheral kick off the script(s) directly, so the application processor doesn&#8217;t have to be bothered with an interrupt every time.</p>
<p>So the system has 48 predefined SDMA events, listed in section 3.3 of the Reference Manual. Each of these events can turn <strong>one or several</strong> channels eligible for executing by automatically setting their EP flag. Which of the channels will have its EP flag set is determined by the SDMA event&#8217;s CHNENBL register. There are 48 such registers, one for each SMDA register, with each of its 32 bits corresponding to an SDMA channel: If bit <em>i</em> is set, the event linked with the register will set EP[i]. Note that these registers have unknown values on powerup, so if event driven SDMA is enabled, all registers must be initialized, or hell breaks loose.</p>
<p>In a normal flow, EP[i] is zero when an event is about to set this flag: If it was set by a previous event, the respective SDMA script should have finished, and hence cleared the flag before the next event occurred. Since attempting to set EP[i] when it&#8217;s already set may indicate that the event came too early (or the script is too late), there&#8217;s an CHNERR[i] flag, which latches such errors, so that the application processor can make itself informed about such a condition. This can also trigger an interrupt, if the respective bit in INTRMASK is set. The application processor can read these flags (and reset them at the same time) in the EVTERR register.</p>
<p>I&#8217;d like to draw special attention to events #14 and #15, which are driven by external pins, namely GPIO1_4 and GPIO1_5. These two make it possible for an external chip (e.g. an FPGA) request service without involving the application processor. A rising edge on these lines creates an event when the IOMUX is set to ALT1 (SDMA_EXT_EVENT) on the relevant pins. Note that setting the IOMUX to just GPIO won&#8217;t do it.</p>
<p>It&#8217;s important to note, that the combination of the EP[i] flag being cleared by the script itself with the edge-triggered nature of the event signal creates an inevitable risk for a race condition: There is no rigorous way for the script to make sure that a &#8220;DONE 4&#8243; instruction, which was intended to clear a previous event won&#8217;t clear one that just arrived to create another. The CHNERR[i] flag will indicate that the event arrived before the previous one was cleared, but in some implementations, that can actually be a legal condition. This can be solved by emulating a level-triggered event with a constantly toggling event line, when the external hardware wants servicing. This will make CHNERR[i] go high for sure, but otherwise it&#8217;s fine.</p>
<p>This possible race condition is not a design bug of the SDMA subsystem. Rather, it was designed with SDMA script which finish faster than the next event in mind. The &#8220;I need service&#8221; kind of design was not considered.</p>
<h3>Interrupts</h3>
<p>By executing a &#8220;DONE 3&#8243; command, the SDMA scripts can generate interrupts on the application processor by setting the HI[i] flag, where i is the channel number of the currently running script. This will assert interrupt #6 on the application processor, which handles it like any other interrupt.</p>
<p>The H[i] flags can be read by the application processor in the INTR register (see section 52.12.3.2 in the Reference Manual). An interrupt handler should scan this register to determine which channel requests an interrupt. There is no masking mechanism for individual H[i]&#8216;s. The global interrupt #6 can be disabled, but an individual channel can&#8217;t be masked from generating interrupts.</p>
<p>If any of the INTRMASK bits is set, the EVTERR register should also be scanned, or at least cleared, since CHNERR[i] conditions generate interrupts which are indistinguishable from H[i] interrupts.</p>
<p>&#8220;DONE 3&#8243;, which is the only instruction available for setting HI[i] also clears HE[i], so it was clearly designed to work with scripts kicked off directly by the application processor. In order to issue an interrupt from a script, which is kicked off by an event, a little trick can be used: According to section 52.21.2 in the Reference Manual (the detail for the DONE instruction), &#8220;DONE 3&#8243; means &#8220;clear HE, set HI for the current channel and reschedule&#8221;. In other words, make the current channel ineligible of execution unless HO[i] is set, and set HI[i] so an interrupt is issued. But event-driven channels <strong>do</strong> have HO[i] set, so clearing HE[i] has no significance whatsoever. According to table 52-4, the context will be saved, and then restored immediately. So there will be a slight waste of time with context writes and reads, but since the most likely instruction following this &#8220;DONE 3&#8243; is a &#8220;DONE 4&#8243; (that is, clear EP[i], the event-driven script has finished), the impact is rather minimal. Anyhow, I still haven&#8217;t tried this for real, but I will soon.</p>
<hr />
<p>So much for part III. You may want to go on with Part IV: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">Running custom SDMA scripts in Linux</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Freescale i.MX51 SDMA tutorial (part II)</title>
		<link>https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/</link>
		<comments>https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 17:53:18 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=2209</guid>
		<description><![CDATA[This is part II of a brief tutorial about the i.MX51&#8242;s SDMA core. The SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly the same, with changes in the registers&#8217; addresses and different chapters in the Reference Manual. This is by no means a replacement for reading the Reference Manual, but rather [...]]]></description>
			<content:encoded><![CDATA[<p>This is part II of a brief tutorial about the i.MX51&#8242;s SDMA core. The   SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly   the same, with changes in the registers&#8217; addresses and different   chapters in the Reference Manual.</p>
<p>This is by no means a  replacement for reading the Reference Manual, but rather an introduction  to make the landing softer. The division into part goes as follows:</p>
<ul>
<li>Part I: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/" target="_blank">Introduction, addressing and the memory map</a></li>
<li>Part II: Contexts, Channels, Scripts and their execution (this page)</li>
<li>Part III: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/" target="_blank">Events and Interrupts</a></li>
<li>Part IV: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">Running custom SDMA scripts in Linux</a></li>
</ul>
<h3>Contexts and channels</h3>
<p>The SDMA&#8217;s purpose is to service requests from hardware or from the application processor. In a way, it&#8217;s like a processor with no idle task, just interrupts. But the way the service is performed is different from interrupt handling.</p>
<p>Let&#8217;s assume that all scripts (those SDMA programs) are already present in the SDMA&#8217;s memory space. They may reside in the on-chip ROM or they&#8217;ve been loaded into RAM. How are they executed?</p>
<p>The answer lies in the <em>contexts</em>: Some of the SDMA&#8217;s RAM space is allocated for containing an array of structures. There are 32 such structures, each occupying 128 bytes (or 32 32-bit words), so all in all this block takes up 4 kB of memory (there&#8217;s a 96-byte variant as well, but we&#8217;ll leave it for now).</p>
<p>These structures do what their name implies: They contain the context of a certain execution thread. In other words, they contain everything that needs to be stored to resume execution at some point, as if it was never stopped. Since the SDMA core doesn&#8217;t have a stack, this information has to go to a fixed place. This includes the program counter, the registers and flags. Section 52.13.4 in the Reference Manual describes this structure in detail.</p>
<p>As mentioned, there&#8217;s an array of 32 of these structures. It means that the SDMA subsystem can maintain 32 contexts, or if you like, resemble a multitasking system with 32 independent threads. Or in SDMA terms: The SDMA core supports 32 DMA <em>channels</em>. This kinda connects with the common concept of DMA channels: Each channel has a certain purpose and particular flow.</p>
<p>The method to kick off a channel, so it will execute a certain script, is to write directly to the channel&#8217;s context structure, and then set up some flags to make it runnable. This is demonstrated in <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">part IV</a>.  Since the context includes the program counter register, this controls where the execution starts. Other registers can be used to pass information to the script (that is, the SDMA &#8220;program&#8221;). What each register means upon such an invocation is up to the script&#8217;s API.</p>
<h3>A script&#8217;s life cycle (scheduling)</h3>
<p>So there are 32 context, each corresponding to 32 channels. What makes a context load into the registers, making its channel&#8217;s script execute? It&#8217;s time to talk about the scheduler. It&#8217;s described in painstaking detail in the Reference Manual, so let&#8217;s stick to the main points.</p>
<p>The scheduler&#8217;s main function is to decide which channel is the most eligible to spend time on the processor core. This decision is relevant only when the SDMA core isn&#8217;t running anything at all (a.k.a. &#8220;sleeping&#8221;) or when the currently running script voluntarily yields the processor. The SDMA core&#8217;s execution is non-preemptive, so the scheduler can&#8217;t force any script to stop running. In other words, if any script is (mistakenly) caught in an infinite loop, all DMA activity is as good as dead, most possibly leading to a complete system hangup. Nothing can force a script to stop running (expect for a reset or the debugger). Just a small thing to bear in mind when writing those scripts.</p>
<p>The SDMA core has a special instruction for yielding the processor, with the mnemonic &#8220;done&#8221;, which takes a parameter for choosing its variant. Two variants of this instructions have earned their own mnemonics, &#8220;yield&#8221; and &#8220;yieldge&#8221;. While &#8220;done&#8221; variant #3 (usually called just &#8220;done&#8221;) always yields the processor, the two others yield it if there are other channels ready for executing with higher priority (or higher-or-equal priority for &#8220;yieldge&#8221;). But never mind the details. The overall picture is that the script runs until it issues a command saying &#8220;you must stop me now&#8221; (as in &#8220;done&#8221;) or &#8220;you may stop me now&#8221; (as in the two other variants).</p>
<p>Yielding only means that the registers are stored back into the context structure (with optimizations to speed this process up) and that another context may be loaded instead of it. Depending on which variant of &#8220;done&#8221; was used, plus some other factors, the scheduler may or may not reschedule the same channel automatically at a later time. That is, the context may be reloaded into the registers. So unless designed otherwise, the opcode directly after the &#8220;done&#8221; instructions will be executed at some later time. Hence a carefully written script never &#8220;ends&#8221;, it just gives up the processor until the next time the relevant channel is scheduled.</p>
<h3>Channel eligibility</h3>
<p>Now let&#8217;s look at what makes a channel eligible for execution. Leaving priority issues aside, let&#8217;s ask what makes a certain channel a candidate for having its context pushed into the SDMA core.</p>
<p>In some cases, the setup is that the channel becomes eligible for execution without any other condition. This is the case for offload memory copy, for example. In other cases, the channel&#8217;s eligibility depends on some hardware event, typically some peripheral requesting service. The latter scenario resembles old-school interrupt handlers, only the interrupt isn&#8217;t serviced by the application processor, but wakes up a service thread (channel) in the SDMA core. And exactly as waking up a thread in a modern operating system doesn&#8217;t cause immediate execution, but rather sets some flag to make the thread eligible for getting a processor time slice, so does the SDMA channel wakeup work: It&#8217;s just a flag telling the scheduler to push the channel&#8217;s context into the SDMA&#8217;s core when it sees fit.</p>
<p>The Reference Manual sums this up in section 52.4.3.5, saying the channel i is eligible to run if and only if the following expression is logical &#8217;1&#8242;:</p>
<p style="text-align: center;"><strong>(HE[i] or HO[i]) and  (EP[i] or EO[i])</strong></p>
<p>where HE[i], HO[i], EP[i], and EO[i] are flags belonging to the i&#8217;th channel. Let&#8217;s take them one by one:</p>
<ul>
<li>HE[i] stands for &#8220;Host Enable&#8221;, and is set and reset by the application processor by writing to registers. It&#8217;s also cleared by the &#8220;done&#8221; instruction, so it&#8217;s suitable for a scenario where the host kicks off a channel, and the script quits it.</li>
<li>EP[i] stands for &#8220;External Peripheral&#8221;, and is set when an external peripheral wants service (more about that mechanism later on). It&#8217;s cleared by one of the &#8220;done&#8221; variants, so this is the flag used when a peripheral kicks off a channel, and the script quits.</li>
<li>HO[i] stands for &#8220;Host override&#8221;, and is controlled solely by a register written to by the application processor. Its purpose is to make the left hand of the expression always true, when we want the channel&#8217;s eligibility be controlled by the peripheral only.</li>
<li>EO[i] stands for &#8220;External override&#8221;, and is like HO[i] in the way it&#8217;s handled. This flag is set when we want the channel&#8217;s eligibility controlled by the host only.</li>
</ul>
<p>There are four registers in the application processor&#8217;s memory space, which are used to alter these flags: STOP_STAT, HSTART, EVTOVR and HOSTOVR. They are outlined in sections 52.12.3.3-52.12.3.7 in the Reference Manual.</p>
<p>The full truth is that there&#8217;s also a DO[i] flag mentioned (controlled by the DSPOVR register), but it must be held &#8217;1&#8242; on i.MX51 devices, so let&#8217;s ignore it.</p>
<p>So if our case is the application processor controlling the i&#8217;th SDMA channel for offload operation, it sets EO[i], clears HO[i], and then sets HE[i] whenever it wants to have the script running. The script may clear HE[i] with a &#8220;done&#8221; instruction, or the application processor may clear it when appropriate. For example, the script can trigger an interrupt on the application processor, which clears the flag (even though I can&#8217;t see when this would be right way to do it).</p>
<p>In the case of channels being started by a peripheral, the application processor sets HO[i] and clears EO[i]. Certain events (as discussed next) set the EP[i] flag directly, and the script&#8217;s &#8220;done&#8221; instruction clears it.</p>
<p>Keep in mind that the script may not run continuously: It should execute &#8220;yield&#8221; instructions every now and then to give other channels a chance to use the SDMA core, but since neither HE[i] nor EP[i] are affected by yields, the script will keep running until it&#8217;s, well, done.</p>
<p>There is a possibility to reset the SDMA core or force a reschedule with the SDMA&#8217;s RESET register, but that&#8217;s really something for emergencies (e.g. a runaway script).</p>
<hr />
<p>So much for part II. You may want to go on with Part III: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/" target="_blank">Events and Interrupts</a></p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Freescale i.MX SDMA tutorial (part I)</title>
		<link>https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/</link>
		<comments>https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 15:52:55 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[NXP (Freescale)]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=2201</guid>
		<description><![CDATA[This is part I of a brief tutorial about the i.MX51&#8242;s SDMA core. The SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly the same, with changes in the registers&#8217; addresses and different chapters in the Reference Manual. Freescale&#8217;s Linux drivers for DMA also vary significantly across different kernel releases. It looks [...]]]></description>
			<content:encoded><![CDATA[<p>This is part I of a brief tutorial about the i.MX51&#8242;s SDMA core. The SDMA for other i.MX devices, e.g. i.MX25, i.MX53 and i.MX6 is exactly the same, with changes in the registers&#8217; addresses and different chapters in the Reference Manual.</p>
<p>Freescale&#8217;s Linux drivers for DMA also vary significantly across different kernel releases. It looks like they had two competing sets of code, and couldn&#8217;t make up their minds which one to publish.</p>
<p>This is by no means a replacement for reading the Reference Manual, but rather an introduction to make the landing softer. The division into part goes as follows:</p>
<ul>
<li>Part I: Introduction, addressing and the memory map (this page)</li>
<li>Part II: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/" target="_blank">Contexts, Channels, Scripts and their execution</a></li>
<li>Part III: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-events-interrupts/" target="_blank">Events and Interrupts</a></li>
<li>Part IV: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">Running custom SDMA scripts in Linux</a></li>
</ul>
<p><strong>NOTE</strong>: For more information, in particular on SDMA for i.MX6 and i.MX7, there&#8217;s a <a href="http://blog.petri.us/sdma-hacking/part-1.html" target="_blank">follow-up post</a> written by Jonah Petri<a href="http://blog.petri.us/sdma-hacking/part-1.html" target="_blank"></a>.</p>
<h3>Introduction</h3>
<p>Behind all the nice words, the SDMA subsystem is just a small and simple RISC processor core, with its private memory space and some specialized functional units. It works side-by-side with the main ARM processor (the <em>application processor</em> henceforth), and pretty much detached from it. Special registers allow the application processor to control the SDMA&#8217;s core, and special commands on the SDMA&#8217;s core allow it to access the application processor&#8217;s memory space and send it interrupts. But in their natural flow, each of these two don&#8217;t interact.</p>
<p>The underlying idea behind the SDMA core is that instead of hardwiring the DMA subsystem&#8217;s capabilities and possible behaviors, why not write small programs (<em>scripts </em>henceforth), which perform the necessary memory operations? By doing so, the possible DMA operations and variants are not predefined by the chip&#8217;s vendor; the classic DMA operations are still possible and available with vendor-supplied scripts, but the DMA subsystem can be literally programmed to do a lot of other things. Offload RAID xoring is an example of something than can be taken off the main processor, as the data is being copied from disk buffers to the peripherals with DMA.</p>
<p>Scripts are kicked off either by some internal event (say, some peripheral has data to offer) or directly by the main processor&#8217;s software (e.g. an offload memcpy). The SDMA processor&#8217;s instruction set is simple, all opcodes occupying exactly 16 bits in program memory. Its assembler can be acquired from Freescale, or you can download my mini-assembler, which is suitable for small projects (in <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">part IV</a>).</p>
<p>Chapter 52 in the Reference Manual is dedicated to the SDMA, but unfortunately it&#8217;s not easy reading. In the hope to clarify a few things, I&#8217;ve written down the basics. Please keep in mind that the purpose of my own project was to perform memory-to-memory transfers triggered autonomously by an external device, so I&#8217;ve given very little attention to the built-in scripts and handling DMA from built-in peripherals.</p>
<h3>Quirky memory issues</h3>
<p>I wouldn&#8217;t usually start the presentation of a processor with its memory map and addressing, but in this case it&#8217;s necessary, as it&#8217;s a major source of confusion.</p>
<p>The SDMA core processor has its own memory space, which is completely detached from the application processor&#8217;s. There are two modes of access to the memory space: Instruction mode and data mode.</p>
<p>Instruction mode is used in the context of jumps, branches and when calling built-in subroutines which were written with program memory in mind. In this mode, the address points at a 16-bit word (which matches the size of an opcode), so the program counter is incremented (by one) between each instruction (except for jumps, of course).</p>
<p>Data mode is used when reading from the SDMA&#8217;s memory (e.g. loading registers) or writing to it. This should not be confused with the application processor&#8217;s memory (the one Linux sees, for example), which is not directly accessible by the SDMA core. In data mode, addressing works on 32-bit words, so incrementing the data mode address (by one) means moving forward <strong>four bytes</strong>.</p>
<p>Instruction mode and data mode addressing points at exactly the same physical memory space. It&#8217;s possible to write data to RAM in data mode, and then execute it as a script, the latter essentially reading from RAM in instruction mode. It&#8217;s important to note, that different addresses will be used for each. This is best explained with a simple example:</p>
<p>Suppose that we want to run a routine (script) written by ourselves. To do so, it has to be copied into the internal RAM first. How to do that is explained in <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-assembler-linux/" target="_blank">part IV</a>, but let&#8217;s assume that we want to execute our script with a JMP instruction to 0x1800. This is 12 kB from the zero-address of the memory map, since the 0x1800 address is given in 16-bit quanta (2 bytes per address count). After the script is loaded in its correct place, we&#8217;ll be able to read the first instruction (as a piece as data) as follows: Set one of the SDMA&#8217;s processor&#8217;s registers to the value 0x0c00, and then load from the address pointed by that register. The address, 0x0c00, is given in 32-bit quanta (4 bytes per address count), so it hits exactly the same place: 12 kB from zero-address. And since we&#8217;re reading 32 bits, we&#8217;ll read the first instruction as well as the second at the same time.</p>
<p>Let&#8217;s say it loud and clear:</p>
<p><span style="color: #ff0000;"><strong>Instruction mode addresses are always double their  data mode equivalents. </strong></span></p>
<p>As for endianess, the SDMA core thinks Big Endian all the way through. That means, that when reading two assembly opcodes from memory in data mode, we get a 32-bit word, for which the first instruction is on bits [31:16] and the instruction following it on bits [15:0].</p>
<h3>The memory map</h3>
<p>Since we&#8217;re at it, and since the Reference Manual has this information spread all over, here&#8217;s a short outline of what&#8217;s mapped where, in data addresses.</p>
<ul>
<li>0x0000-0x03ff: 4 kB of internal ROM with boot code and standard routines</li>
<li>0x0400-0x07ff: 4 kB of reserved space. No access at all should take place here</li>
<li>0x0800-0x0bff: 4 kB of internal RAM, containing the 32 channels’  contexts (each context is 32 words of 4 bytes each, when SMSZ is set in  the CHN0ADDR register). More about this in <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/" target="_blank">part II</a>. For the details, see Section 52.13.4 in the Reference Manual. When SMSZ is clear, this segment is 3 kB only (see 52.4.4).</li>
<li>0x0c00-0x0fff: 4 kB of internal RAM, free for end-user application scripts and data.</li>
<li>0x1000-0x6fff: Peripherals 1-6 memory space</li>
<li>0x7000-0x7fff: SDMA registers, as accessed directly by the SDMA core (as detailed in section 52.14 of the reference manual)</li>
<li>0x8000-0xffff: Peripherals 7-14 memory space (not accessible in program memory space)</li>
</ul>
<p>The two regions of peripherals memory space is the preferred way to access peripherals (unlike the implementation in Linux drivers using SDMA script) as discussed in <a title="NXP / Freescale SDMA and the art of accessing peripheral registers" href="https://billauer.se/blog/2017/08/nxp-freescale-sdma-memory-map-throughput/" target="_blank">another post of mine</a>.</p>
<p>And once again: The memory map above is  given in <strong>data addresses</strong>. The memory map in program memory  space is the same, only all addresses are <strong>double</strong>.</p>
<hr />
<p>So much for part I. You may want to go on with Part II: <a href="https://billauer.se/blog/2011/10/imx-sdma-howto-channels-scripts/" target="_blank">Contexts, Channels, Scripts and their execution</a></p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2011/10/imx-sdma-howto-memory-map/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
