Перейти к содержанию
    

и зачем это все?
Чтобы проц имел доступ к последним данным, а не к тем, которые были когда-то до тех пор, пока FIFO не забилось.

ТС спросил можно ли сделать такое фифо, а вы уже все обсудили и интерфейсы, и АЦП, и прочее...
При таком подходе - согласен. Но, вообще говоря, из задачи не совсем ясна конечная цель ТС. Правильность того или иного подхода будет сильно зависеть от прочих условий.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

как я понял у ТС однопортовая память SRAM

Да! Одно портовая память срам! Хочу создать модуль контроллера срам, чтоб с ней работать как с фифо, но писать в неё по фронту сигнала WD который приходит из другого модуля, а по спаду WD сбрасывать в регистр и хранить там пока не перелью данные в мк и не произведу следующий запрос чтения.

Вот начал писать модуль сий. Затык с этим самым "автоматом состояний" который должен активировать регистр rdy когда посылаем запрос и ожидаем данные, вот тут за спотыкался.

 

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 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

при старте, если указатели чтения и записи равны (т.е. записей в озу нет), то первая запись должна идти напрямую в dout.

empty при этом падает в 0.

следующие записи если empty в нуле идут в sram, счетчик записей увеличивается.

если активны циклы записи, а микроконтроллер увидев не empty решил почитать, то он читает dout, и empty становится в 1 до момента, когда будет возможность прочитать данные из sram в dout.

в ближайший возможный момент когда записи нет, а empty в 1, и есть записи в озу - из sram вычитывается в dout, empty падает в 0.

 

идея понятна?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

при старте, если указатели чтения и записи равны (т.е. записей в озу нет), то первая запись должна идти напрямую в dout.

empty при этом падает в 0.

следующие записи если empty в нуле идут в sram, счетчик записей увеличивается.

если активны циклы записи, а микроконтроллер увидев не empty решил почитать, то он читает dout, и empty становится в 1 до момента, когда будет возможность прочитать данные из sram в dout.

в ближайший возможный момент когда записи нет, а empty в 1, из sram вычитывается в dout, empty падает в 0.

 

идея понятна?

Оооо.. Точно!!!! Спасибо!!! Действительно в начале должны данные на выход захлопнутся, а уж потом сливаться в sram. Ведь если первым зашёл то первым и выйти должен по принципу фифо

 

Вобщем суть такая данные у меня заливаются в плис по 8 бит шине, а sram 16бит это дало мне возможность написать модуль который забирает данные по 8 бит и складывает в регистр 16 бит для того чтоб залить их в sram и плюс время целых два такта на то чтоб можно было по одному из них записывать, а по другому считывать.

Вот текст этой прослойки типа

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Удалось реализовать такой модуль FIFO на внешней SRAM, но работает он как то не стабильно и через раз!

Уважаемые спецы, помогите найти причины не стабильности.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

я полагаю, никто до сих пор не понял какого конечного результата вы хотите добиться =)

 

Значит, у Вас такой задачи не стояло :biggrin:

 

Автору топика: вполне заурядная задача, смелее делайте и будет всё работать.

Не забудьте про довольно большие счетчики адресов, их сравнение, и результат этого сравнения.

 

Тут правильно советуют - операция записи в ОЗУ имеет приоритет перед чтением.

Работу с ОЗУ разбиваем на циклы. Цикл состоит из записи и потом чтения.

Если есть запрос на запись - пишем. Если есть запрос на чтение - читаем, пока не появится запрос на запись или не кончится чтение.

Если одновременно присутствует и запрос на запись, и на чтение, то в каждом цикле одно слово должно записаться, и одно-вычитаться.

Тут надо кропотливо поработать с таймингами ОЗУ.

Очень помогает моделирование в Моделсиме или что там у Вас есть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тут правильно советуют - операция записи в ОЗУ имеет приоритет перед чтением.

Работу с ОЗУ разбиваем на циклы. Цикл состоит из записи и потом чтения.

Если есть запрос на запись - пишем. Если есть запрос на чтение - читаем, пока не появится запрос на запись или не кончится чтение.

Если одновременно присутствует и запрос на запись, и на чтение, то в каждом цикле одно слово должно записаться, и одно-вычитаться.

Тут надо кропотливо поработать с таймингами ОЗУ.

Так получается тут нужно реализовывать конечный автомат состояний?

Проблема видимо в том что у меня два клоковых домина и я не соображу как мне преодалеть эту проблему. У меня есть основной клок в CPLD 100Mhz. Частота записи 50Mhz а частота чтения 10Mhz. Эти частоты я реализовал делителями.

Была идея:

Запись в абсолютном приоритете!! Если поступил запрос на чтение то мы ждём пока кончится запись и как только это произойдёт то мы прочитаем данные на выход и выставим флаг готовности данных, сбросив при этом флаги запроса. Но при таком раскладе у меня в RTL модели видны защёлки, а это явная ошибка проекта.

Как же быть?

 

Я просто чувствую что это вполне реализуемо, но нет идей и помощи более опытных товарищей. :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так получается тут нужно реализовывать конечный автомат состояний?

Я просто чувствую что это вполне реализуемо, но нет идей и помощи более опытных товарищей. :laughing:

Да, конечный автомат нужен. Но он простой, один-два триггера.

 

Вы с частотами-то поосторожнее. Тщательно просчитайте, возможно ли в принипе добиться от Вашей памяти такой растактовки. Тут нужен в клеточку лист бумаги, карандаш и мозг. Оно само всё покажет. Если основной клок 100МГц и данные поступают на каждом втором такте - то у Вас просто нет шансов даже на запись - каким образом сформировать управляющие сигналы? Или делать асинхронщину на RC цепях. У Вас нету системного подхода. Сначала - растактовка памяти (управляющие сигналы формируются из виртуального клока, и это будет явно не 100МГц, а 180, 166 или рядом). Критерий - максимально выжать быстродействие ОЗУ при условии, что управление идет почти на макс скорости (половина системного клока).

 

Вижу сам, что сумбурно. Вы сами сразу всё увидите, как растактовку ОЗУ нарисуете.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, конечный автомат нужен. Но он простой, один-два триггера.

 

Вы с частотами-то поосторожнее. Тщательно просчитайте, возможно ли в принипе добиться от Вашей памяти такой растактовки. Тут нужен в клеточку лист бумаги, карандаш и мозг. Оно само всё покажет. Если основной клок 100МГц и данные поступают на каждом втором такте - то у Вас просто нет шансов даже на запись - каким образом сформировать управляющие сигналы? Или делать асинхронщину на RC цепях. У Вас нету системного подхода. Сначала - растактовка памяти (управляющие сигналы формируются из виртуального клока, и это будет явно не 100МГц, а 180, 166 или рядом). Критерий - максимально выжать быстродействие ОЗУ при условии, что управление идет почти на макс скорости (половина системного клока).

 

Вижу сам, что сумбурно. Вы сами сразу всё увидите, как растактовку ОЗУ нарисуете.

Да вроде работает, но видно что что то не то. Сложность заключается в проблеме моделирования шин с 3мя состояниями. В железе видно что работает но не так как хотелось бы

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если основной клок 100МГц и данные поступают на каждом втором такте - то у Вас просто нет шансов даже на запись - каким образом сформировать управляющие сигналы?
Но данные приходят по 8 бит, а ширина шины данных ОЗУ 16 бит, что даёт ему возможность накопить слово и записать его одним махом. А пока оно копится - прочитать 16 бит для выдачи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но данные приходят по 8 бит, а ширина шины данных ОЗУ 16 бит, что даёт ему возможность накопить слово и записать его одним махом. А пока оно копится - прочитать 16 бит для выдачи.

Именно так я и делаю!!! На частоте 100 я собераю данные по 8 бит, в регистр 16 бит. И того частота делится надвое. получается 50 Mhz. Память у меня 16ти разрядная и тем самым частота записи снижается. Читать я буду на частоте 10Mhz вот такой расклад получается, но проблема то в том что у меня 2 клоковых домина пересекающийся между собой!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Читать я буду на частоте 10Mhz вот такой расклад получается, но проблема то в том что у меня 2 клоковых домина пересекающийся между собой!
Ну тогда читать Вам надо по системе "запрос-ответ". Т.е. некий мастер выставляет запрос на чтение по определённому адресу, ваша ПЛИС "принимает" этот запрос и ожидает паузы между записями. Наступает пауза максимум через 4 такта, Вы читаете из ОЗУ и выставляете готовность - "забирай" :)

Более того, учитывая, что читаете Вы на 10МГц, у Вас, как навскидку думается, есть все шансы - обработать "запрос на чтение" в этом же цикле чтения, не заставляя читающее устройство ждать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

задача проста как 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. То есть на след такте вы их легко считаете...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

задача проста как 2 копейки, вы мудрите...

первое: делаете 2 портовую память.

- это просто, она на частоте 100 МГц, то пишет в 1 адрес (если есть сигнал записи, или не пишите), то выставляет 2 адрес и читает. Получаете 50 МГц 2 портовую память.

У меня не двух портовая память CY7C1041DV33

У неё нет такой возможности обращения к одним и тем же данным по разным портам

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У меня не двух портовая память...
Дык, Вам и пишут - как из обычного 100МГц ОЗУ сделать а-ля двухпортовое 50МГц ОЗУ. Только порты будут фактически на ПЛИСке.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...