Доступ к памяти осуществляется блоком управления памятью (MMU), который содержит контроллер прямого доступа к памяти (DMA), позволяющий передавать блоки данных между блоками памяти. Эти блоки могут быть выделены для DRAM (динамическая оперативная память), RAM ЦП (также известной как кеш уровня 1, 2 и 3) и периферийных устройств среди прочего — все зависит от вашей структуры таблицы памяти.

Поскольку литография постепенно достигает своего предела, а токи утечки КМОП по-прежнему имеют большое значение, мы должны искать повышение производительности в другом месте.

Следующий код, если он не находится в критическом контексте, будет ждать своей очереди для выполнения (подумайте о времени ожидания переключения контекста), а затем будет ждать своей очереди, чтобы получить доступ к MMU, который, в свою очередь, получит доступ к DRAM, потратив сотни, если не тысячи часов. циклов, просто чтобы читать по одному байту за раз из области памяти, на которую указывает src. Тот же процесс перезапускается для обратной записи в область памяти, на которую указывает dest. Этот процесс повторяется cnt раз.

По этой причине программист попытается установить максимально возможный размер копируемых блоков памяти. Вот пример на ассемблере PowerPC:

В приведенном выше примере у нас есть выровненный по 4 байтам источник (контроллер управления системой) и выровненный по 8 байтам пункт назначения, поэтому 4 чтения и 2 записи — это много накладных расходов! По этой причине лучше иметь все страницы памяти с одинаковыми размерами выравнивания.

Перейдите к решению для более быстрого memcpy: DMA. Три регистра, флаг управления и флаг состояния (для простоты мы пока будем игнорировать выравнивание памяти), а также необязательное приращение исходного адреса и необязательное приращение адреса назначения.

библиотека IEEE;

используйте IEEE.STD_LOGIC_1164.ALL;

используйте IEEE.STD_LOGIC_ARITH.ALL;

используйте IEEE.STD_LOGIC_UNSIGNED.ALL;

объект DMAcontroller является

порт (

во-первых, sys_clk : IN std_logic; — сброс и системные часы

reg_addr : IN std_logic_vector (от 3 до 0);

reg_data : INOUT std_logic_vector (от 7 до 0);

reg_we : в std_logic;

mem_we : OUT std_logic; — разрешение на запись и разрешение на чтение для доступа к памяти

mem_addr : OUT std_logic_vector (от 31 до 0); — адресная шина

mem_data : INOUT std_logic_vector (от 7 до 0);

mem_req : OUT std_logic; — запрос доступа к памяти

mem_ack, mem_rdy : IN std_logic — подтверждение доступа к памяти и сигнал готовности данных

);

конечный DMA-контроллер;

архитектура Architecture_DMAcontroller контроллера DMA

тип state_t (IDLE, REQ_MEM, READ_MEM, WRITE_PREP, WRITE_MEM, DEC_CTR);

состояние сигнала, n_state : state_t;

атрибут syn_encoding : строка; атрибут syn_encoding состояния : сигнал «серый»;

сигнал sar, dar, n_sar, n_dar: std_logic_vector (от 31 до 0);

сигнал sctlr, temp: std_logic_vector (7 вниз к 0);

счетчик сигналов, n_counter : std_logic_vector (15 до 0);

начинать

процесс (первый, sys_clk)

начинать

если rst = ‘0’ то — сброс лучше при активном низком уровне для экономии циклов на POR

состояние ‹= бездействует;

счетчик ‹= (другие =› ‘0’);

sar ‹= (другие =› ‘0’);

dar ‹= (другие =› ‘0’);

sctlr(7 до 2) ‹= (другие =› ‘0’);

sctlr(0) ‹= ‘0’;

темп ‹= (другие =› ‘0’);

elsif Rising_Edge(sys_clk) then — активный сигнал нарастающего фронта

состояние ‹= n_state;

reg_data ‹= (другие =› ‘Z’); — установить направление регистра внутрь для ввода по умолчанию

состояние случая

когда IDLE =›

если reg_we = ‘1’, то

случай reg_addr

при «0000» =› — регистр состояния/управления

sctlr(7 меньше 2) ‹= reg_data(7 меньше 2);

sctlr(0) ‹= reg_data(0);

когда «0010» =› — адресный регистр источника

sar(7 до 0) ‹= reg_data;

когда «0011» =› — адресный регистр источника

sar(15 до 8) ‹= reg_data;

когда «0100» =› — адресный регистр источника

sar(23 до 16) ‹= reg_data;

когда «0101» =› — адресный регистр источника

sar(от 31 до 24) ‹= reg_data;

когда «0110» =› — регистр адреса получателя

dar(7 до 0) ‹= reg_data;

когда «0111» =› — регистр адреса получателя

dar(15 до 8) ‹= reg_data;

когда «1000» =› — регистр адреса получателя

dar(23 до 16) ‹= reg_data;

когда «1001» =› — регистр адреса получателя

dar(от 31 до 24) ‹= reg_data;

когда «1010» =› — регистр счетчика

counter(7 вниз до 0) ‹= reg_data;

когда «1011» =› — регистр счетчика

счетчик(от 15 до 8) ‹= reg_data;

когда другие =›

конечный случай;

еще

случай reg_addr

когда «0000» =›

reg_data ‹= sctlr;

когда «0010» =›

reg_data ‹= sar(7 до 0);

когда «0011» =›

reg_data ‹= sar(15 до 8);

когда «0100» =›

reg_data ‹= sar(23 до 16);

когда «0101» =›

reg_data ‹= sar(от 31 до 24);

когда «0110» =›

reg_data ‹= dar(7 до 0);

когда «0111» =›

reg_data ‹= dar(15 до 8);

когда «1000» =›

reg_data ‹= dar(23 до 16);

когда «1001» =›

reg_data ‹= dar(от 31 до 24);

когда «1010» =›

reg_data ‹= счетчик (от 7 до 0);

когда «1011» =›

reg_data ‹= счетчик (от 15 до 8);

когда другие =›

конечный случай;

закончить, если;

когда READ_MEM =›

если mem_rdy = ‘1’, то

темп ‹= данные_памяти;

закончить, если;

когда DEC_CTR =›

счетчик ‹= n_счетчик;

если sctlr(2) = ‘1’, то — включить приращение адреса на источнике

сар ‹= н_сар;

закончить, если;

если sctlr(3) = ‘1’, то — включить увеличение адреса на месте назначения

дар ‹= н_дар;

закончить, если;

когда другие =›

конечный случай;

закончить, если;

завершить процесс;

процесс(состояние, sctlr, mem_ack, mem_rdy, temp, counter, dar, sar)

начинать

n_state ‹= состояние; — по умолчанию назначить следующее состояние = текущее состояние

mem_addr ‹= (другие =› ‘Z’);

mem_data ‹= (другие =› ‘Z’);

mem_we ‹= ‘Z’;

состояние случая

когда IDLE =› — состояние ожидания — ждем DMA запроса

если sctlr(0) = ‘1’, то

n_state ‹= REQ_MEM;

закончить, если;

когда REQ_MEM =› — запросить доступ к шине памяти

если mem_ack = ‘1’, то — запрос подтвержден, переход к следующему состоянию

n_state ‹= READ_MEM;

закончить, если;

mem_we ‹= ‘0’; — включить чтение

адрес_памяти ‹= sar;

когда READ_MEM =› — зафиксировать данные во временном регистре

если mem_rdy = ‘1’, то

n_state ‹= WRITE_MEM;

закончить, если;

mem_we ‹= ‘0’;

адрес_памяти ‹= sar;

когда WRITE_PREP =› — подготовить данные из временного регистра на шину памяти

mem_we ‹= ‘1’;

mem_data ‹= темп;

n_state ‹= WRITE_MEM;

адрес_памяти ‹= дар;

когда WRITE_MEM =›

mem_we ‹= ‘1’;

адрес_памяти ‹= дар;

mem_data ‹= темп;

если mem_rdy = ‘1’, то

n_state ‹= DEC_CTR;

закончить, если;

когда DEC_CTR =›

если счетчик = X”00, то

n_state ‹= IDLE;

еще

n_state ‹= REQ_MEM;

закончить, если;

когда другие =›

n_state ‹= IDLE;

конечный случай;

завершить процесс;

sctlr(1) ‹= ‘0’, когда состояние = IDLE, иначе ‘1’; - сигнал занято

mem_req ‹= ‘0’, когда состояние = IDLE, иначе ‘1’;

n_counter ‹= (counter — X”0001) when (counter › X”00) else counter; - иметь готовый сигнал на переднем фронте дает выигрыш в производительности

n_sar ‹= (sar + X”0001);

n_dar ‹= (dar + X”0001);

конец архитектуры_DMAcontroller;

Синтез в Synplify Pro дает желаемый результат, наблюдаемый в следующем FSM:

Спасибо, что нашли время, чтобы прочитать, и я был бы признателен за любую критику.