Доступ к памяти осуществляется блоком управления памятью (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:
Спасибо, что нашли время, чтобы прочитать, и я был бы признателен за любую критику.