인프런 커뮤니티 질문&답변

Jeongwung Jeong님의 프로필 이미지
Jeongwung Jeong

작성한 질문수

설계독학맛비's 실전 Verilog HDL Season 1 (Clock부터 Internal Memory까지)

[HDL 20장] Internal Memory Interface 에 대해 이해해보자 (FPGA 의 BRAM 을 이해하기 - 실습편)

simple_bram_ctrl.v에 true_dpbram.v를 instantiation 을 어떻게 해야 할까요?

작성

·

605

1

안녕하세요 

simple_bram_ctrl.v 의 148 째줄 밑에 아래와 같이

true_dpbram.v를 instantiation을  해서 시뮬레이션을 돌리면 test bench에서 콘트롤러 모듈만 instantiation해 주면 되겠다 싶어서 나름 응용해 보고자 아래 처럼 instantiation 해줘서 vivado에서 implantation을 해 줬는데요 자꾸 error가 나오네요.

제생각에는 콘트롤러의 q0 포트가 input 인데 true_dpbram 모듈의 q0는 out put이라서 그런거 같은데 포트를 어떻게 매치 시켜줄야 instantiation이 될까요?

  아래는 에러 메세지 입니다.

답변 2

1

제공해 주신 simple_bram_ctrl.v 마지막에 true_dpbram.v 를  instantiation 한 코드만 아래와 같이추가한 단순한 코드입니다.  

전체 코드도 첨부 하였습니다.

true_dpbram 

#( .DWIDTH   (DWIDTH), 

.AWIDTH   (AWIDTH), 

.MEM_SIZE (MEM_SIZE)) 

u_TDPBRAM(

.clk (clk), 

 

.addr0 (addr0), 

.ce0 (ce0), 

.we0 (we0), 

.q0 (q0), 

.d0 (d0), 

 

// no use port B.

.addr1 (0), 

.ce1 (0), 

.we1 (0),

.q1 (), 

.d1 (0)

);

//////////////////////////////////////////////////////////////////////////////////
// Company: Personal
// Engineer: Matbi / Austin
//
// Create Date: 2021.01.31
// Design Name: 
// Module Name: simple_bram_ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description: To study ctrl sram. (WRITE / READ)
//				FSM + mem I/F
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
 
`timescale 1ns / 1ps
module simple_bram_ctrl
// Param
#(
	parameter DWIDTH = 16,
	parameter AWIDTH = 7,
	parameter MEM_SIZE = 128
)

(
    input 				clk,
    input 				reset_n,
	input 				i_run,
	input  [AWIDTH-1:0]	i_num_cnt,
	output   			o_idle,
	output   			o_write,
	output   			o_read,
	output  			o_done,

// Memory I/F
	output[AWIDTH-1:0] 	addr0,
	output 				ce0,
	output 				we0,
	input [DWIDTH-1:0]  q0,
	output[DWIDTH-1:0] 	d0,

// output read value from BRAM
	output 				o_valid,
	output[DWIDTH-1:0] 	o_mem_data

    );


/////// Local Param. to define state ////////
localparam S_IDLE	= 2'b00;
localparam S_WRITE	= 2'b01;
localparam S_READ	= 2'b10;
localparam S_DONE  	= 2'b11;

/////// Type ////////
reg [1:0] c_state; // Current state  (F/F)
reg [1:0] n_state; // Next state (Variable in Combinational Logic)
wire	  is_write_done;
wire	  is_read_done;

/////// Main ////////

// Step 1. always block to update state 
always @(posedge clk or negedge reset_n) begin
    if(!reset_n) begin
		c_state <= S_IDLE;
    end else begin
		c_state <= n_state;
    end
end

// Step 2. always block to compute n_state
//always @(c_state or i_run or is_done) 
always @(*) 
begin
	n_state = c_state; // To prevent Latch.
	case(c_state)
	S_IDLE	: if(i_run)
				n_state = S_WRITE;
	S_WRITE : if(is_write_done)
				n_state = S_READ;
	S_READ  : if(is_read_done)
				n_state = S_DONE;
	S_DONE	: n_state = S_IDLE;
	endcase
end 

// Step 3.  always block to compute output
// Added to communicate with control signals.
assign o_idle 		= (c_state == S_IDLE);
assign o_write 		= (c_state == S_WRITE);
assign o_read 		= (c_state == S_READ);
assign o_done 		= (c_state == S_DONE);

// Step 4. Registering (Capture) number of Count
reg [AWIDTH-1:0] num_cnt;  
always @(posedge clk or negedge reset_n) begin
    if(!reset_n) begin
        num_cnt <= 0;  
    end else if (i_run) begin
        num_cnt <= i_num_cnt;
	end else if (o_done) begin
		num_cnt <= 0;
	end
end

// Step 5. increased addr_cnt
reg [AWIDTH-1:0] addr_cnt;  
assign is_write_done = o_write && (addr_cnt == num_cnt-1);
assign is_read_done  = o_read  && (addr_cnt == num_cnt-1);

always @(posedge clk or negedge reset_n) begin
    if(!reset_n) begin
        addr_cnt <= 0;  
    end else if (is_write_done || is_read_done) begin
        addr_cnt <= 0; 
    end else if (o_write || o_read) begin
        addr_cnt <= addr_cnt + 1;
	end
end

// Assign Memory I/F
assign addr0 	= addr_cnt;
assign ce0 		= o_write || o_read;
assign we0 		= o_write;
assign d0		= addr_cnt;  // same value;


// output data from memory 
reg 				r_valid;
reg [DWIDTH-1:0] 	r_mem_data;

// 1 cycle latency to sync mem output
always @(posedge clk or negedge reset_n) begin
    if(!reset_n) begin
        r_valid <= 0;  
    end else begin
		r_valid <= o_read; // read data
	end
end

assign o_valid = r_valid;
assign o_mem_data = q0;  // direct assign, bus Matbi recommends you to add a register for timing.

true_dpbram 
#(	.DWIDTH   (DWIDTH), 
	.AWIDTH   (AWIDTH), 
	.MEM_SIZE (MEM_SIZE)) 
u_TDPBRAM(
	.clk		(clk), 

	.addr0		(addr0), 
	.ce0		(ce0), 
	.we0		(we0), 
	.q0			(q0), 
	.d0			(d0), 

// no use port B.
	.addr1 		(0), 
	.ce1		(0), 
	.we1		(0),
	.q1			(), 
	.d1			(0)
);

endmodule

설계독학맛비님의 프로필 이미지
설계독학맛비
지식공유자

아하..... 

바로 파악했구요.

그래서 이렇게 적어주셨군요.

제생각에는 콘트롤러의 q0 포트가 input 인데 true_dpbram 모듈의 q0는 out put이라서 그런거 같은데 포트를 어떻게 매치 시켜줄야 instantiation이 될까요?

제가 답을 알려드리기 전에 이 그림에 대해서 생각해 보시겠어요? (먼저 20장 그림을 가져왔습니다.)

제가 몰라서... 일 수도 있는데!!! (실력이 부족한 맛비... ㅠ.ㅠ)

질문자님께서 스스로 찾으시기를 바라는 마음에서 적습니다.

후배님께 설계 Tip 을 드리자면, Verilog HDL 을 코딩하기 전에 Architecture 를 먼저 고민하시기를 바랍니다.

지금 이 상황을 다음처럼 바꾸고 싶어하시는 것 같아요.

Hint 를 드리자면,

.

.

.

.

.

.

DUT 에서 Port A 에 대한 in /out port 가 필요할까요?

Wire 로 변경하여서, port 를 연결한다면? 어떻게 될까요?

고민해보시고, 코드를 수정해서 다시 남겨주세요.

아 참고로! 못맞추셔도 됩니다.  (괴롭히는거 아니에요. 오해하지 말아주세요.)

시도한 내용을 정리해서 남겨주세요.

우리는 공부하는 입장이니까요!!

즐공입니다. :)

true_dpbram 모듈의 q0를 o_mem_data에 연결해 주니 instantiation 이 되네요. 너무 쉽게 해결 되네요. input qO를 어떻게든 이용해야 한다고 생각이 고정 되어있던 것 같습니다. 조언 해 주신 대로ㅐoutput q0를 버리고 바로 o_mem_data에 연결해 주면 되는 되는데요^^ 

아래는 instatntiation을 위한 변형해본 코드 입니다.

저는 개인적으로는 두개의 모듈을 instantiation 해서 하나의 모듈로 만들어서 비바도에서 implantation도 해 볼수 있어서 더 좋았던 것 같습니다. 같지만 다른 두 디자인인데 각자 어떤 잇점이 있을 까요? 강의에서 보여주신 것 처럼 두개 의 dotv 파일을 tb로 묵어서 시뮬레이션을 보여주신 숨은 의도? 가 있을 까요?^^

assign o_valid = r_valid;
//assign o_mem_data = q0;  // direct assign, bus Matbi recommends you to add a register for timing.

true_dpbram 
#(	.DWIDTH   (DWIDTH), 
	.AWIDTH   (AWIDTH), 
	.MEM_SIZE (MEM_SIZE)) 
u_TDPBRAM(
	.clk		(clk), 

	.addr0		(addr0), 
	.ce0		(ce0), 
	.we0		(we0), 
	.q0			(o_mem_data), 
	.d0			(d0), 

// no use port B.
	.addr1 		(0), 
	.ce1		(0), 
	.we1		(0),
	.q1			(), 
	.d1			(0)
);

설계독학맛비님의 프로필 이미지
설계독학맛비
지식공유자

안녕하세요  :)

먼저, 코드 수정 부분은.

지금 처럼 모듈 내부에 Memory 가 들어가는 상황에서 "memory port 를 in / output 으로 사용할 필요가 없다" 였구요. 그렇게 해결 하셨으리라 믿습니다.

강의에서 보여주신 것 처럼 두개 의 dotv 파일을 tb로 묵어서 시뮬레이션을 보여주신 숨은 의도? 가 있을 까요?^^

"Memory Controller 를 설계해보자" 여서, Memory 를 바깥으로 빼서 설계해 봤어요.  만약에 질문자님이 설계자이다. 그런데 넣는게 좋아보인다. 그러면 넣으시면 됩니다. (설계자 마음대로!)

하지만, 현업가시면, Memory 는 모아서 관리합니다.
즉, 제 강의 코드처럼 Memory 는 바깥에 있다 라는 가정하에 설계하실 가능성이 높아요.

그런데, 지금은 공부하는 입장이니까, 이런 부분에 연연? 하시지 마시고, 마음껏 많이 설계해 보세요.

그럼 즐공하세요~~ :)

0

설계독학맛비님의 프로필 이미지
설계독학맛비
지식공유자

안녕하세요 :)

Multiple Driver Nets 는 input, output port 를 중복 사용을 해서 그런 것 같구요.

현재 캡쳐해주신 내용만으로는 잘 모르겠습니다.

q0 에 관한 내용 전부를 확인해보고 싶어요.

추가적으로 코드를 남겨주시겠어요?

많은 정보를 주시면 해결에 도움이 될 것 같아요 :)

(코드를 전부 올려주시는 것도 방법)

Jeongwung Jeong님의 프로필 이미지
Jeongwung Jeong

작성한 질문수

질문하기