LINUX.ORG.RU

Шина данных в Verilog

 ,


0

2

Пытаюсь сделать простейший 16-битный процессор на ПЛИС. Для начала пытаюсь реализовать шину данных, по которой ядро будет взаимодействовать с переферией (USART, GPIO и т. д.). И не получается.

Вот так сейчас выглядит ядро:

module mcu_core(rst, clk, addr, data, write);
	input rst;
	input clk;
	output reg[15:0] addr;
	inout[15:0] data;
	output reg write;
	
	reg[15:0] data_out;
	assign data = write ? data_out : 16'bZ;
	
	reg[31:0] counter;
	reg[3:0] tmp;
	
	always @(posedge clk or negedge rst)
		if (~rst) begin
			addr <= 0;
			data_out <= 0;
			write <= 0;
			counter <= 0;
		end else begin
			if (counter == 0) begin
				counter <= 60 * 1000 * 1000;
			end else begin
				counter <= counter - 1;
				if (counter == 1) begin
					addr <= 16'hF010;
					data_out <= tmp;
					write <= 1;
				end else if (counter == 30 * 1000 * 1000) begin
					addr <= 16'hF010;
					write <= 0;
					tmp <= ~data;
				end else begin
					write <= 0;
					addr <= 0;
					data_out <= 0;
				end
			end
		end
endmodule

Пока вместо исполнения программы из памяти используются хардкоженные действия. Ядро пытается прочитать 16 бит по адресу 0xF010, инвертировать их и записать. И делать так каждую секунду. К шине подключен модуль управления 4 светодиодами:

module led_module(rst, clk, addr, data, write, led);
	input rst;
	input clk;
	input[15:0] addr;
	inout[15:0] data;
	input write;
	output wire[3:0] led;
	
	wire cs;
	reg[3:0] state;
	assign cs = (addr == 16'hF010);
	assign led = ~state;
	assign data = (~write && cs) ? state : 16'bZ;
	
	always @(posedge clk or negedge rst)
		if (~rst) begin
			state <= 0;
		end else begin
			if (cs && write) begin
				state <= data;
			end
		end
endmodule

Он хранит текущее состояние светодиодов в буфере и позволяет читать и писать его. А реальные выводы ПЛИС синхронизированы с буфером (светодиоды подключены к ПЛИС катодом, поэтому для правильной работы выполняется инверсия).

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

Запись без чтения работает - если писать константное значение вместо tmp, то я могу управлять светодиодами.

Подсоединение ядра и модуля светодиодов к шине в модуле верхнего уровня выполнено так:

wire[15:0] addr;
wire[15:0] data;
wire write;
	
mcu_core core0(usb_rst, usb_clk, addr, data, write);
led_module leds(usb_rst, usb_clk, addr, data, write, led);
★★★★★

Уже подзабыл все.

counter == 30 * 1000 * 1000

Вот в этот момент (и только в этот) cpu у тебя читает данные. А они к этому моменту уже готовы? У тебя выставление адреса, выставление данных модулем led для чтения, само чтение и сигнал write одновременно происходят по posedge. Такая пока догадка без проверки. Ты хотя бы попробуй сначала сделать write <= 0 и выставление адреса, а на заднем фронте clock (negedge) данные считать из data, т. е. разведи по времени.

addr <= 16'hF010;
write <= 0;

и 

tmp <= ~data;
Zubok ★★★★★ ()
Последнее исправление: Zubok (всего исправлений: 3)
Ответ на: комментарий от Zubok

Угу. Это помогло. Но насколько это правильно разделять работу с шиной по фронтам? Это не создаст проблем, когда я буду уже писать реальное исполнение инструкций?

KivApple ★★★★★ ()
Ответ на: комментарий от KivApple

Это все от задумки зависит. Про negedge я посоветовал только для быстрой проверки. Всегда надо иметь в виду, что в реальном мире сигналы мгновенно не выставляются и мгновенно не распространяются. САПР обсчитывает модель как раз с учетом *реальных* задержек в ПЛИС. Я просто обращаю на это внимание. Это повод про это почитать и проектировать с учетом этого.

Взгляни любой datasheet на любой микроконтроллер или процессор, на временные диаграммы циклов чтения-записи в порты. Там все о задержках (время выставления сигнала, время распространения) и везде эти задержки специфицированы, через какое время появляются данные после выставления адреса и пр.

Zubok ★★★★★ ()
Ответ на: комментарий от Zubok

Это все от задумки зависит.

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

Zubok ★★★★★ ()
Ответ на: комментарий от KivApple

Как то это все сложно тут сделано. Во-первых для внутренней шины не стоит использовать логику с третьем состоянием. Не знаю как в альтере, у ксилинкса она лет 15 как не рекомендуется. Асинхронный ресет тоже — фи. Два фронта это не очень хорошая идея.

Посмотри как сделаны готовые шины, например, Avalon, AXI или wishbone если хочется опенсорса.

В данном конкретном примере cs доедет до led_module только через некоторое время, поэтому все единицы в tmp правильно, ибо так выглядит третье состояние.

ebantrop ()
Ответ на: комментарий от ebantrop

Во-первых для внутренней шины не стоит использовать логику с третьем состоянием. Не знаю как в альтере, у ксилинкса она лет 15 как не рекомендуется.

в альтере так же. внутри - только in и out; inout только во внешнем модуле с добавлением tristate буфера

registrant ★★★★★ ()
Ответ на: комментарий от KivApple

Могут возникнуть проблемы, если увлечёшься и не заметишь, что у тебя где-то один и тот же сигнал может стробироваться и по фронту клока, и по спаду в разных кусках кода. Такая конструкция является несинтезируемой. Для неё в плисине должны физически существовать триггеры, которые могут работать одновременно на фронте и спаде тактовых импульсов, а их скорее всего нет.

Имхо лучше вообще так не писать и сделать конвейер — выдать/считать значение в два такта.

prischeyadro ★★★☆☆ ()

Я сейчас тоже проц на верилоге пишу, правда 32 битный и похожий на мипс 1. Дай свои контакты(я уже спрашивал, но как-то все заглохло), вместе веселее пилить будет.

Deleted ()
Ответ на: комментарий от ebantrop

Я верно понимаю, что надо сделать так:

module led_module(rst, clk, addr, data_r, data_w, write, led);
	input rst;
	input clk;
	input[15:0] addr;
	output[15:0] data_r;
	input[15:0] data_w;
	input write;
	output wire[3:0] led;
	
	wire cs;
	reg[3:0] state;
	assign cs = (addr == 16'hF010);
	assign led = ~state;
	assign data_r = (~write && cs) ? state : 16'bZ;
	
	always @(posedge clk)
		if (~rst) begin
			state <= 4'b0;
		end else begin
			if (cs && write) begin
				state <= data_w;
			end
		end
endmodule

В ядре просто data_r и data_w имеют противоположные типы input и output.

Да? А можно ещё советов, как лучше писать (каких конструкций избегать, а какие наоборот очень хороши в общем случае)? А то данное изменение уменьшило количество синтезированных блоков.

KivApple ★★★★★ ()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

Третье состояние не используй. Его нет внутри современных ПЛИСов. Поставь внешний мультиплексор.

По поводу почитать не могу сейчас найти. Было что то со словами hdl best practices или methodology. Или просто в мануале по синтезу рассказано что и как.

ebantrop ()
Ответ на: комментарий от KivApple

У xilinx, например, есть тристейт буффер, погугли IOBUF. Я бы что-то такое изобразил, но за 100% корректность сейчас не ручаюсь.

module led_module(rst, clk, addr, data_r, data_w, write_enable, read_enable, led);
	input clk;
	input[15:0] addr;
        inout [15:0] data;
	input write_enable;
        input read_enable;
	output wire[3:0] led;
	
        reg [15:0] data_in;
	wire [15:0] data_in_w;
	wire [15:0] data_out_w;
	assign data_in_w[15:0] = data_in[15:0];

	wire cs;
	assign cs = (addr == 16'hF010);
        wire tristate_ctl;
        assign tristate_ctl = cs && !write_enable;

	IOBUF data_buf[15:0](
		.T(tristate_ctl),
		.I(data_out_w),
		.IO(data),
		.O(data_in_w)
	);



	always @ (posedge clk)
		if (!write_enable && cs)
                begin
                    state <= data_in_w[3:0];
                end

               if (!read_enable && cs)
               begin
                   data_out <= ~state;
               end
endmodule

Deleted ()
Ответ на: комментарий от ebantrop

Согласен. Я, наверное, неправильно понял изначальную задачу. Тогда IOBUF, полагаю, можно заменить на какой-нибудь свой костыль с похожим интерфейсом и возможностями.

Deleted ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.