嵌入式外设之DMA

目录

 

DMA简介

【整理】以快递为例来说明DMA的功能

DMA本意解析

为何会出现DMA?

DMA使用示例

DMA vs 快递

关于DMA是需要硬件支持的

总结


DMA简介

DMA

不是独立的某个外设,而是一个硬件模块

支持DMA的功能

一般对应的,也是按个数来的,对应的叫做多少个通道channel。

 

【整理】以快递为例来说明DMA的功能

DMA本意解析

DMA==Direct Memory Access==直接存储器访问

Direct:直接,对应的就有间接:之前都是,CPU参与,一点点把数据,从一个地方拷贝,即像搬家一样搬到,另一个地方

很明显,此时,相对时间比较宝贵(比较值钱)的CPU,把时间,就用在(浪费在)拷贝数据了。

 

Memory:存储器

一般多数都指的是内存

当然,DMA也会涉及到,外设的一些Buffer,数据寄存器等等操作

Access:访问

即操作上面所提到的,存储器

即数据的读写,所以要访问,操作对应的存储器

 

为何会出现DMA?

所以,尤其很明显可以看出:

之前就是觉得,对于数据拷贝这样,相对低级的,简单的任务,

结果却要,时间比较值钱的CPU,去干这样的“杂货”

就有点浪费CPU的时间了

所以,才出现这个DMA

专门去干,拷贝数据这个活

由此,释放了CPU,CPU就可以去干其他的,相对更加有价值(值钱的)事情了

 

DMA使用示例

比如,拿uboot中的

S3c2410_nand.c (drivers\mtd\nand) 4513 2013/10/17

中的:

nand_read_buf

为例来说明:

之前就只是CPU去一点点的,慢慢的读数据:

?

1

2

3

4

5

6

7

8

static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)

{

    int i;

    struct nand_chip *this = mtd->priv;

 

    for (i = 0; i < len; i++)

        buf[i] = readb(this->IO_ADDR_R);

}

而如果是改为DMA

则只需要:

CPU去配置好DMA

然后DMA自动会去读数据

就不用CPU再操心了

就不用CPU再费时间去读数据了。

CPU就有空去执行其他更重要的事情了。

 

另外再举个例子:

之前已经实现的,linux的kernel中的nand flash驱动中的dma的例子:

在对应的hwecc的read函数:

as353x_nand_read_page_hwecc

中,当开启了DMA的READ和普通read的相关代码为:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

static int as353x_nand_read_page_hwecc(struct mtd_info *mtd,

            struct nand_chip *chip, uint8_t *buf)

{

...

#ifdef NAF_USE_DMA_READ

    info->len = mtd->writesize;

 

    /* map CPU buffer(virtual address) to DMA buffer(DMA address) */

    info->txaddr = dma_map_single(   info->device,

                                    (void *)buf,

                                    info->len,/* here len is in bytes, not in words !*/

                                    DMA_FROM_DEVICE);

    if (unlikely(dma_mapping_error(info->device, info->txaddr))) {

        ret = -ENOMEM;

        dev_err(info->device, "DMA read map failed\n");

        goto dma_map_err;

    }

 

    /* apply DMA slave and client */

    as353x_nand_dma_init_tx(info);

 

    /* set info for use in call back */

    info->callback_param.client_info = (void *)info;

 

    /* init values */

    info->txsg.length        = info->len;

    info->txsg.page_link = 0;

    info->txsg.offset        = 0;

    info->txsg.dma_address = info->txaddr;

 

    desc = info->txchan->device->device_prep_slave_sg(

                            info->txchan,

                            &info->txsg,

                            1,

                            DMA_FROM_DEVICE,

                            DMA_PREP_INTERRUPT);

    if ( unlikely(!desc) ) {

        dev_err(info->device, "Unable to get descriptor for DMA read\n");

        ret = -EBUSY;

        goto prep_sg_err;

    }

 

    desc->callback = as353x_nand_dma_complete_callback;

    desc->callback_param = &info->callback_param;

    desc->tx_submit(desc);

    /* inform dma controller to start */

    info->txchan->device->device_issue_pending(info->txchan);

 

    /* inform nand controller to start */

    set_bit32(NAF_CFG_DMA_ON, info->regs + NAF_CONFIG);

 

    if (unlikely(!wait_for_completion_timeout(&info->done,

                                msecs_to_jiffies(1 * 1000)))) {

        dev_err(info->device, "DMA read timeout\n");

        clear_bit32(NAF_CFG_DMA_ON, info->regs + NAF_CONFIG);

        ret = -ENXIO;

        info->txchan->device->device_free_chan_resources(info->txchan);

        info->txchan = NULL;

        AS353XNAND_DBG("len=%d, read=%x\n", info->len, info->txcount);

        as353x_nand_show_reg(mtd);

        goto dma_timeout_err;

    }

#else

    /* none-DMA trasfer */

 

    /* read page data */

    as353x_nand_read_buf_hwbch4(mtd, buf,   mtd->writesize);

#endif

...

}

 

/* for read, from nand to buffer, using DMA_FROM_DEVICE */

static void as353x_nand_dma_init_tx(struct as353x_nand_info *info)

{

    struct dma_client *txclient;

    struct dma_slave *txslave;

 

    txslave = &info->txslave;

    txclient = &info->txclient;

 

    txslave->tx_reg = 0;

    txslave->rx_reg = info->dmabase + NAF_FIFODATA;

    txslave->reg_width = DMA_SLAVE_WIDTH_32BIT;

    txslave->dev = info->device;

    txclient->event_callback     = as353x_nand_dma_req_tx_chan_callback;

    dma_cap_set(DMA_SLAVE, txclient->cap_mask);

    txclient->slave = txslave;

    dma_async_client_register(txclient);

    dma_async_client_chan_request(txclient);

}

 

static void as353x_nand_read_buf_hwbch4(struct mtd_info *mtd,

            u_char *buf, int len)

{

    int i, j;

    u32 *buf_u32 = (u32 *)buf;

    struct as353x_nand_info *info = as353x_nand_mtd_toinfo(mtd);

 

    /* to words */

    len = BYTE2WORD(len);

 

    for ( j = ( len / NAF_FIFO_FILLSIZE_IN_WORDS ); j > 0; --j ) {

        /* wait for fifo to get filled (again) - with high speed flashes this */

        as353x_nand_wait_until_almost_full(mtd);

         

        for ( i = 0; i < NAF_FIFO_FILLSIZE_IN_WORDS; i++ )

        {

            *buf_u32 = readl(info->regs + NAF_FIFODATA);

            ++buf_u32;

        }

    }

 

    /* if any words left in FIFO in case of

    non n-times words of NAF_FIFO_FILLSIZE_IN_WORDS */

    for ( i = 0; i < ( len % NAF_FIFO_FILLSIZE_IN_WORDS ); i++) {

        while (as353x_fifo_isempty(mtd)); /* wait for FIFO is filled */

        *buf_u32 = readl(info->regs + NAF_FIFODATA);

        ++buf_u32;

    }

}

可见,当不用DMA时,对应就是去:

对应的读取寄存器中的数据

?

1

readl(info->regs + NAF_FIFODATA)

而开启了DMA的话,则是去利用对应的DMA驱动中,申请对应的DMA通道和资源,

然后再去用DMA去传输数据的。

DMA中,对应的指定源地址是:

?

1

txslave->rx_reg = info->dmabase + NAF_FIFODATA;

目的地址则是对应的,一点点增加的,每次增加的是32bit=4字节:

?

1

txslave->reg_width = DMA_SLAVE_WIDTH_32BIT;

 

DMA vs 快递

由此可看出,其实DMA,和快递,很类似:

快递:

你的需求是:

想要送东西,从某地到某地

想要通过快递去实现此需求

而之所以选择快递而不自己去送,

有的是自己没时间;

有的是自己有时间,但是成本更高,不值得花在送东西这上面:

    比如,寄点东西,本身就只值100元,打算从南京送到北京,快递的话,可能也就10元,20元就够了,而要自己去送,单独火车票,甚至飞机票,都要几百,甚至几千。所以,还是通过快递公司送东西,更划算。

有的是,自己有时间,但是自己的时间,更值钱,不值得花在送东西这上面:

    假如你是身家不菲,比如比尔盖茨,即使不嫌弃自己送东西的成本更高,但是也是自己的时间浪费不起,自己的时间,如果花费在送东西上,加起来会值更多的钱,所以不值得自己把宝贵的时间,用在送东西的小事情上面,所以还是选择快递更合适。

等等情况。

对于快递来说:

其优点是:

对于多数用户来说,选择快递寄东西,成本更低,更经济,更划算;

而快递对于用户来说,其所关心的是:

告诉其目的地:对应的,起始的出发地点,在你送东西时,就已经知道了,所以不用再问你

告诉其价格:用户只要支持对应的价格,快递就可以寄送了。

由此类似的DMA:

CPU,就像DMA的用户

CPU的时间很值钱,在有DMA的前提下,

还是把数据拷贝这个事情,交个DMA去做,更经济,更划算。

然后CPU就有空去做其他更值钱,更有意义的事情了。

而对于DMA来说:

其只需要CPU配置好DMA,DMA就可以去干活了,就可以去搬运数据了。

而CPU配置DMA,实际上就是告诉DMA:

搬运数据的起始地址和目标地址:就类似于送快递时的,出发点和目的地

而关于快递时用户要支付的价格,对于CPU来说,表面上是没有去额外给DMA什么补偿的。

只不过,对于整个系统来说,如果你的CPU可以借用DMA去传数据,那么:

系统中是要存在DMA这个硬件(模块,功能)的:这对于设计系统的硬件时,是否增加DMA功能,本身就是成本和效率方面的衡量后的考虑;

不过,CPU使用DMA传输数据,和用户使用快递寄东西,有些方面不太一样:

  • 速度

CPU使用DMA传输数据,往往是为了提高CPU利用率,而结果,更重要的是:

DMA传输数据的话,速度更快,效率更高;

对应的用户选择快递寄东西,有时候,未必是比自己亲自去送,的速度更快,花的时间更短。

但是总的来说,往往是最经济的。

  • DMA通道个数是有限的

现实中的快递公司,除了大的节假日之外,对于普通用户来说,那处理能力,基本都是无限的。

不会由于你多寄了个东西,快递公司,就忙不过来了。

而现实中的DMA,其资源是有限的:

DMA的个数,是按照通道channel来算的;

同一时刻,一个channel的DMA,只能做一件数据搬家的工作;

而且,往往是:

一个嵌入式系统中,往往很多内部功能模块,都希望有机会用到DMA,但是实际上DMA通道个数有限,

使得很难都满足其需求。

所以,在DMA的使用上,是需要你程序设计者去决定哪个模块使用DMA,然后在对应的驱动中,将数据拷贝的功能,用DMA来实现,以此提升性能的。

不过,另外,一般情况下,也是有对应的DMA驱动,去管理DMA资源,使得只要错开同时使用,也是以可以使得多个模块,都能用到DMA的。但是,往往系统中,某个模块用DMA的话,都是相对比较频繁的,因为是很多时候都在处理数据拷贝,所以往往是某个模块,要是用DMA的话,都是独占单个的DMA通道的。

比如:

系统中,假如只有一个通道的DMA的话,

而Nand Flash中,SD卡驱动中,都希望用到,那么:

你只能根据自己的需求去决定:

假如我的嵌入式系统,物理上的主要的存储设备是Nand Flash

为了提升系统性能,决定把DMA给Nand Flash使用

在保证系统整体的性能相对较好的前提下,而对于SD卡,只是用于存储用户数据,速度稍微慢一点,其也是能接受的。

 

关于DMA是需要硬件支持的

比如AS3536中,就支持:

AHB1中的8个DMA的channel,16个request:

as3536 ahb1 dma ahb2 peripheral request

 

AHB2中有8个DMAchannel,32个request:

ahb2 dma controller request 0 to 16 channels

ahb2 dma controller request 17 to 31 channels

 

 

总结

DMA,资源有限,需要合理利用。

且需硬件支持。

转载请注明:在路上 » 【整理】嵌入式外设之DMA

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值