|
|
  |
FIFO (CPLD+SRAM) |
|
|
|
Sep 7 2015, 06:06
|

Знающий
   
Группа: Свой
Сообщений: 597
Регистрация: 24-05-06
Из: г. Чебоксары
Пользователь №: 17 402

|
Цитата(Golikov A. @ Sep 7 2015, 08:36)  и зачем это все? Чтобы проц имел доступ к последним данным, а не к тем, которые были когда-то до тех пор, пока FIFO не забилось. Цитата(Golikov A. @ Sep 7 2015, 08:36)  ТС спросил можно ли сделать такое фифо, а вы уже все обсудили и интерфейсы, и АЦП, и прочее... При таком подходе - согласен. Но, вообще говоря, из задачи не совсем ясна конечная цель ТС. Правильность того или иного подхода будет сильно зависеть от прочих условий.
--------------------
Почему разработчики систем повышенной надёжности плохо справляются с простыми проектами? :)
|
|
|
|
|
Sep 7 2015, 06:57
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Цитата(Maverick @ Sep 7 2015, 10:02)  как я понял у ТС однопортовая память SRAM Да! Одно портовая память срам! Хочу создать модуль контроллера срам, чтоб с ней работать как с фифо, но писать в неё по фронту сигнала WD который приходит из другого модуля, а по спаду WD сбрасывать в регистр и хранить там пока не перелью данные в мк и не произведу следующий запрос чтения. Вот начал писать модуль сий. Затык с этим самым "автоматом состояний" который должен активировать регистр rdy когда посылаем запрос и ожидаем данные, вот тут за спотыкался. CODE module sram_control( input clk_pld, //основной такт плис input wd, //запись в sram, (высокий приоритет) input request, //запрос данных output reg ack, //подтверждение готовности данных output empty, //sram пустая (читать нельзя) output full, //sram полная (писать нельзя) output [17:0] addr, //адрес sram input [15:0] din, //вход данных для записи во внешнюю sram output reg [15:0] dout, //выход данных для чтения из sram inout [15:0] dinout //двунаправленный порт данных для подключения непосредственно к sram );
reg rdy = 0; reg [15:0] buff = 0; //буфер 3х состояний reg [18:0] count_w = 0; //регистр счётчика записи reg [18:0] count_r = 0; //регистр счётчика чтения
//счётчик адреса записи always @(posedge clk) begin if(wd) count_w = count_w + 1; end
//счётчик адреса чтения always @(posedge clk) begin if(rdу) count_r = count_r + 1; end
//процес управляющий логикой чтения данных always @(posedge clk) begin if(request) begin //?????????????????????????????????? end end
always @(posedge clk) begin if(wd) buff = din; else begin buff = 16'bz; if(rdу) dout = dinout; end end
assign dinout = buff; assign addr = (!wd && rdу)? count_r : count_w;
assign full = ((count_w[17:0] == count_r[17:0]) && (count_w[18] ^^ count_r[18]))? 1'b1 : 1'b0; assign empty = ((count_w[17:0] == count_r[17:0]) && (!(count_w[18] ^^ count_r[18])))? 1'b1 : 1'b0; endmodule
|
|
|
|
|
Sep 7 2015, 07:22
|
Профессионал
    
Группа: Свой
Сообщений: 1 700
Регистрация: 2-07-12
Из: дефолт-сити
Пользователь №: 72 596

|
при старте, если указатели чтения и записи равны (т.е. записей в озу нет), то первая запись должна идти напрямую в dout. empty при этом падает в 0. следующие записи если empty в нуле идут в sram, счетчик записей увеличивается. если активны циклы записи, а микроконтроллер увидев не empty решил почитать, то он читает dout, и empty становится в 1 до момента, когда будет возможность прочитать данные из sram в dout. в ближайший возможный момент когда записи нет, а empty в 1, и есть записи в озу - из sram вычитывается в dout, empty падает в 0.
идея понятна?
--------------------
провоцируем неудовлетворенных провокаторов с удовольствием.
|
|
|
|
|
Sep 7 2015, 08:21
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Цитата(krux @ Sep 7 2015, 11:22)  при старте, если указатели чтения и записи равны (т.е. записей в озу нет), то первая запись должна идти напрямую в dout. empty при этом падает в 0. следующие записи если empty в нуле идут в sram, счетчик записей увеличивается. если активны циклы записи, а микроконтроллер увидев не empty решил почитать, то он читает dout, и empty становится в 1 до момента, когда будет возможность прочитать данные из sram в dout. в ближайший возможный момент когда записи нет, а empty в 1, из sram вычитывается в dout, empty падает в 0.
идея понятна? Оооо.. Точно!!!! Спасибо!!! Действительно в начале должны данные на выход захлопнутся, а уж потом сливаться в sram. Ведь если первым зашёл то первым и выйти должен по принципу фифо Вобщем суть такая данные у меня заливаются в плис по 8 бит шине, а sram 16бит это дало мне возможность написать модуль который забирает данные по 8 бит и складывает в регистр 16 бит для того чтоб залить их в sram и плюс время целых два такта на то чтоб можно было по одному из них записывать, а по другому считывать. Вот текст этой прослойки типа CODE module translator( input wire clk, //клок input wire [7:0]sbyte, //вход 1байт input wire rdy, //загрузка байта output reg [15:0]word, //выход 2байта output reg wr //данные готовы );
reg cnt = 0; reg [15:0]buff0 = 0;
always @(posedge rdy) begin if(!cnt) begin buff0[15:8] = sbyte[7:0]; end else begin buff0[7:0] = sbyte[7:0]; word = buff0; end cnt = cnt + 1'b1; end
always @(posedge clk) begin wr = ~cnt && rdy; end
endmodule
|
|
|
|
|
Sep 24 2015, 12:44
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Удалось реализовать такой модуль FIFO на внешней SRAM, но работает он как то не стабильно и через раз! Уважаемые спецы, помогите найти причины не стабильности. CODE module FIFO_SRAM( input Reset, input Wclk, input Rclk, input Rd, input Wd, output full, output empty, input [DATA-1:0] Din, output reg [DATA-1:0] Dout, output [ADDR-1:0] Addr_Sram, inout [DATA-1:0] Data_Sram, output nCS, output nOE, output nWE );
parameter ADDR = 4; // Параметр разрядности Адреса SRAM parameter DATA = 4; // Параметр разрядности Данных SRAM
//--- Счётчики адресов чтения и записи ------------------------------ assign Addr_Sram = (Wd) ? write_addr[ADDR-1 : 0] : read_addr[ADDR-1 : 0]; reg [ADDR:0] read_addr=0; always @(posedge Rclk) //По такту чтения begin if(Reset) begin read_addr <= 0; end else begin if (Rd) read_addr <= read_addr + 1'b1; else read_addr <= read_addr; end end //--- reg [ADDR:0] write_addr=0; always @(posedge Wclk) //По такту записи begin if(Reset) begin write_addr <= 0; end else begin if (Wd) write_addr <= write_addr + 1'b1; else write_addr <= write_addr; end end //--- Абработка флагов заполненности ФИФО ------------------------------ assign full = ((write_addr[ADDR-1:0] == read_addr[ADDR-1:0]) && (write_addr[ADDR] ^ read_addr[ADDR]))? 1'b1 : 1'b0; //Фифо полное assign empty = ((write_addr[ADDR-1:0] == read_addr[ADDR-1:0]) && (!(write_addr[ADDR] ^ read_addr[ADDR])))? 1'b1 : 1'b0; //Фифо пустое
//--- Логика комутации данных ------------------------------ wire [DATA-1 : 0] bus_data_out; always@(posedge Wd or posedge Rd) begin Dout <= bus_data_out; //При любом запросе пишим на выход end
assign bus_data_out = (Wd & empty)? Din : 'bz ; //Если запись и фифо пустое то со входа пишем на прямую в выходной регистр assign Data_Sram = (Wd & ~empty)? Din : 'bz ; //Если запись и в выходном регистре не пусто то заполняем SRAM assign bus_data_out = (~Wd & Rd)? Data_Sram : 'bz ; //Если нет записи, но есть чтение то из SRAM в выходной регистр
assign nCS = ~(Wd | Rd); //Разрешение чипа SRAM assign nOE = ~(Rd); //Чтение чипа SRAM assign nWE = ~(Wd); //Запись чипа SRAM
endmodule
|
|
|
|
|
Sep 24 2015, 13:04
|

Местный
  
Группа: Свой
Сообщений: 449
Регистрация: 28-10-04
Из: Украина
Пользователь №: 1 002

|
Цитата(krux @ Sep 5 2015, 20:10)  я полагаю, никто до сих пор не понял какого конечного результата вы хотите добиться =) Значит, у Вас такой задачи не стояло Автору топика: вполне заурядная задача, смелее делайте и будет всё работать. Не забудьте про довольно большие счетчики адресов, их сравнение, и результат этого сравнения. Тут правильно советуют - операция записи в ОЗУ имеет приоритет перед чтением. Работу с ОЗУ разбиваем на циклы. Цикл состоит из записи и потом чтения. Если есть запрос на запись - пишем. Если есть запрос на чтение - читаем, пока не появится запрос на запись или не кончится чтение. Если одновременно присутствует и запрос на запись, и на чтение, то в каждом цикле одно слово должно записаться, и одно-вычитаться. Тут надо кропотливо поработать с таймингами ОЗУ. Очень помогает моделирование в Моделсиме или что там у Вас есть.
--------------------
Умею молчать на 37 языках...
|
|
|
|
|
Sep 24 2015, 13:22
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Цитата(Gorby @ Sep 24 2015, 17:04)  Тут правильно советуют - операция записи в ОЗУ имеет приоритет перед чтением. Работу с ОЗУ разбиваем на циклы. Цикл состоит из записи и потом чтения. Если есть запрос на запись - пишем. Если есть запрос на чтение - читаем, пока не появится запрос на запись или не кончится чтение. Если одновременно присутствует и запрос на запись, и на чтение, то в каждом цикле одно слово должно записаться, и одно-вычитаться. Тут надо кропотливо поработать с таймингами ОЗУ. Так получается тут нужно реализовывать конечный автомат состояний? Проблема видимо в том что у меня два клоковых домина и я не соображу как мне преодалеть эту проблему. У меня есть основной клок в CPLD 100Mhz. Частота записи 50Mhz а частота чтения 10Mhz. Эти частоты я реализовал делителями. Была идея: Запись в абсолютном приоритете!! Если поступил запрос на чтение то мы ждём пока кончится запись и как только это произойдёт то мы прочитаем данные на выход и выставим флаг готовности данных, сбросив при этом флаги запроса. Но при таком раскладе у меня в RTL модели видны защёлки, а это явная ошибка проекта. Как же быть? Я просто чувствую что это вполне реализуемо, но нет идей и помощи более опытных товарищей.
|
|
|
|
|
Sep 24 2015, 13:49
|

Местный
  
Группа: Свой
Сообщений: 449
Регистрация: 28-10-04
Из: Украина
Пользователь №: 1 002

|
Цитата(uragan90 @ Sep 24 2015, 17:22)  Так получается тут нужно реализовывать конечный автомат состояний? Я просто чувствую что это вполне реализуемо, но нет идей и помощи более опытных товарищей.  Да, конечный автомат нужен. Но он простой, один-два триггера. Вы с частотами-то поосторожнее. Тщательно просчитайте, возможно ли в принипе добиться от Вашей памяти такой растактовки. Тут нужен в клеточку лист бумаги, карандаш и мозг. Оно само всё покажет. Если основной клок 100МГц и данные поступают на каждом втором такте - то у Вас просто нет шансов даже на запись - каким образом сформировать управляющие сигналы? Или делать асинхронщину на RC цепях. У Вас нету системного подхода. Сначала - растактовка памяти (управляющие сигналы формируются из виртуального клока, и это будет явно не 100МГц, а 180, 166 или рядом). Критерий - максимально выжать быстродействие ОЗУ при условии, что управление идет почти на макс скорости (половина системного клока). Вижу сам, что сумбурно. Вы сами сразу всё увидите, как растактовку ОЗУ нарисуете.
--------------------
Умею молчать на 37 языках...
|
|
|
|
|
Sep 24 2015, 13:58
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Цитата(Gorby @ Sep 24 2015, 17:49)  Да, конечный автомат нужен. Но он простой, один-два триггера.
Вы с частотами-то поосторожнее. Тщательно просчитайте, возможно ли в принипе добиться от Вашей памяти такой растактовки. Тут нужен в клеточку лист бумаги, карандаш и мозг. Оно само всё покажет. Если основной клок 100МГц и данные поступают на каждом втором такте - то у Вас просто нет шансов даже на запись - каким образом сформировать управляющие сигналы? Или делать асинхронщину на RC цепях. У Вас нету системного подхода. Сначала - растактовка памяти (управляющие сигналы формируются из виртуального клока, и это будет явно не 100МГц, а 180, 166 или рядом). Критерий - максимально выжать быстродействие ОЗУ при условии, что управление идет почти на макс скорости (половина системного клока).
Вижу сам, что сумбурно. Вы сами сразу всё увидите, как растактовку ОЗУ нарисуете. Да вроде работает, но видно что что то не то. Сложность заключается в проблеме моделирования шин с 3мя состояниями. В железе видно что работает но не так как хотелось бы
|
|
|
|
|
Sep 24 2015, 14:09
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Цитата(EvgenyNik @ Sep 24 2015, 18:04)  Но данные приходят по 8 бит, а ширина шины данных ОЗУ 16 бит, что даёт ему возможность накопить слово и записать его одним махом. А пока оно копится - прочитать 16 бит для выдачи. Именно так я и делаю!!! На частоте 100 я собераю данные по 8 бит, в регистр 16 бит. И того частота делится надвое. получается 50 Mhz. Память у меня 16ти разрядная и тем самым частота записи снижается. Читать я буду на частоте 10Mhz вот такой расклад получается, но проблема то в том что у меня 2 клоковых домина пересекающийся между собой!
|
|
|
|
|
Sep 24 2015, 15:10
|

Знающий
   
Группа: Свой
Сообщений: 597
Регистрация: 24-05-06
Из: г. Чебоксары
Пользователь №: 17 402

|
Цитата(uragan90 @ Sep 24 2015, 17:09)  Читать я буду на частоте 10Mhz вот такой расклад получается, но проблема то в том что у меня 2 клоковых домина пересекающийся между собой! Ну тогда читать Вам надо по системе "запрос-ответ". Т.е. некий мастер выставляет запрос на чтение по определённому адресу, ваша ПЛИС "принимает" этот запрос и ожидает паузы между записями. Наступает пауза максимум через 4 такта, Вы читаете из ОЗУ и выставляете готовность - "забирай"  Более того, учитывая, что читаете Вы на 10МГц, у Вас, как навскидку думается, есть все шансы - обработать "запрос на чтение" в этом же цикле чтения, не заставляя читающее устройство ждать.
--------------------
Почему разработчики систем повышенной надёжности плохо справляются с простыми проектами? :)
|
|
|
|
|
Sep 24 2015, 19:32
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
задача проста как 2 копейки, вы мудрите... первое: делаете 2 портовую память. - это просто, она на частоте 100 МГц, то пишет в 1 адрес (если есть сигнал записи, или не пишите), то выставляет 2 адрес и читает. Получаете 50 МГц 2 портовую память. схема,без размерности и может быть с ошибками, но суть должна быть понятна Код input addr1; input addr2; input we; input data_in; output data_out; reg PortSel; //регистр для выбора порта reg mem_data_in; //регистр для сохранения
always @(posedge clk) begin if(PortSel == 0) begin mem_addr <= addr2; //готовимся читать со 2 адреса mem_we <= 1'b0; //снимаем сигнал записи, 2 адрес чтение data_out <= mem_data_out; //защелкиваем данные с памяти (с прошлого задания адреса) PortSel <= 1'b1; end else begin mem_addr <= addr1; //готовимся писать в 1 адрес если надо mem_we <= we; //ставим we если надо mem_data_in <= data_in; //защелкиваем данные для записи PortSel <= 1'b0; end end А после того как вы это сделали, у вас остается сделать правильный переход из домена 100 МГц в домен 10. Это просто запрос на чтение через 2 триггера, из 10 в 100, там выставляете защелкиваете данные на выход, через 1 клок данные будут валидны и стоять на входе 10. То есть на след такте вы их легко считаете...
|
|
|
|
|
Sep 25 2015, 07:45
|
Местный
  
Группа: Свой
Сообщений: 248
Регистрация: 2-02-09
Из: Тверь
Пользователь №: 44 309

|
Цитата(Golikov A. @ Sep 24 2015, 23:32)  задача проста как 2 копейки, вы мудрите... первое: делаете 2 портовую память. - это просто, она на частоте 100 МГц, то пишет в 1 адрес (если есть сигнал записи, или не пишите), то выставляет 2 адрес и читает. Получаете 50 МГц 2 портовую память. У меня не двух портовая память CY7C1041DV33 У неё нет такой возможности обращения к одним и тем же данным по разным портам
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|