해결된 질문
작성
·
59
·
수정됨
0
안녕하세요 삼코치님! I2C SLAVE 코드에서 질문이 있습니다.
1. 데이터를 송수신 하는 아래 코드에는 시스템 클럭에 동기화된 SCL와 SDA를 사용했는데, Start와 Stop 조건 감지에는 SDA와 SCL 신호를 그대로 사용한 이유를 모르겠습니다. 또한 동기화하는 이유에 대해서도 명확히 모르겠습니다.
2.데이터를 송수신하는 코드에서는 I2C 수신하는 부분은 Posedge를 사용하고, 마스터로 송신하는 부분은 Negedge를 사용했는데, 따로 이유가 있는지 궁금합니다.
3.always 블럭 내부에서 blocking assignment를 사용했는데, 시스템 클럭과 동기화된 SCL을 클럭으로 사용한다면 Non-blocking으로 사용해야 하는 것 아닌가요?
한 번에 많이 여쭤봐서 죄송합니다. 강의 잘 듣고 있습니다. 감사합니다!
--------------------코드------------------------
module I2C_SLAVE(CLCK, SCL, SDA);
//I2C
input CLCK;
input SCL;
inout SDA; //bidirectional port
parameter slaveaddress = 7'b1110010;
//Sample registers to send to requesting device
reg[2:0] valuecnt = 3'b011; //Count of bytes to be sent, send read value twice
//Synch SCL edge to the CPLD clock
reg [2:0] SCLSynch = 3'b000;
always @(posedge CLCK)
SCLSynch <= {SCLSynch[1:0], SCL};
wire SCL_posedge = (SCLSynch[2:1] == 2'b01);
wire SCL_negedge = (SCLSynch[2:1] == 2'b10);
//Synch SDA to the CPLD clock
reg [2:0] SDASynch = 3'b000;
always @(posedge CLCK)
SDASynch <= {SDASynch[1:0], SDA};
wire SDA_synched = SDASynch[0] & SDASynch[1] & SDASynch[2];
//Detect start and stop
reg start = 1'b0;
always @(negedge SDA)
start = SCL; //
reg stop = 1'b0;
always @(posedge SDA)
stop = SCL; //
//Set cycle state
reg incycle = 1'b0;
always @(posedge start or posedge stop)
if (start)
begin
if (incycle == 1'b0)
incycle = 1'b1;
end
else if (stop)
begin
if (incycle == 1'b1)
incycle = 1'b0;
end
//Address and incomming data handling
reg[7:0] bitcount = 0;
reg[6:0] address = 7'b0000000;
reg[7:0] datain = 8'b00000000;
reg rw = 1'b0;
reg addressmatch = 1'b0;
always @(posedge SCL_posedge or negedge incycle)
if (~incycle)
begin
//Reset the bit counter at the end of a sequence
bitcount = 0;
end
else
begin //
bitcount = bitcount + 1;
//Get the address
if (bitcount < 8)
address[7 - bitcount] = SDA_synched;
if (bitcount == 8)
begin
rw = SDA_synched;
addressmatch = (slaveaddress == address) ? 1'b1 : 1'b0;
end
if ((bitcount > 9) & (~rw))
datain[17 - bitcount] = SDA_synched;
end
//ACK's and out going data
reg sdadata = 1'bz;
reg [2:0] currvalue = 0;
always @(posedge SCL_negedge)
//ACK's
if (((bitcount == 8) | ((bitcount == 17) & ~rw)) & (addressmatch))
begin
sdadata = 1'b0;
currvalue = 0;
end
//Data
else if ((bitcount >= 9) & (rw) & (addressmatch) & (currvalue < valuecnt))
begin
//Send Data
if (((bitcount - 9) - (currvalue * 9)) == 8)
begin
//Release SDA so master can ACK/NAK
sdadata = 1'bz;
currvalue = currvalue + 1;
end
else sdadata = datain[7 - ((bitcount - 9) - (currvalue * 9))]; //Modify this to send actual data, currently echoing incomming data valuecnt times
end
//Nothing (cause nothing tastes like fresca)
else sdadata = 1'bz;
assign SDA = sdadata;
endmodule
답변 1
0
네 안녕하세요, 답변 남겨드립니다.
먼저, I2C의 Start와 Stop 조건을 감지할 때 SDA와 SCL 신호를 별도의 동기화 없이 직접 사용하는 이유는 I2C 신호가 마스터의 제어에 의해 시스템 클럭과는 무관하게 비동기적으로 동작하기 때문입니다. Start 조건은 SCL이 high인 상태에서 SDA가 falling edge일 때 발생하고, Stop 조건은 SCL이 high인 상태에서 SDA가 rising edge일 때 발생합니다. 이 조건들을 정확히 감지하려면 SDA의 상승 또는 하강 에지를 실시간으로 감지해야 하므로, 시스템 클럭에 동기화된 신호를 사용할 경우 타이밍에 따라 Start 또는 Stop 이벤트를 놓칠 가능성이 있습니다. 따라서 이러한 이벤트는 동기화된 신호가 아닌 원래의 SDA와 SCL을 직접 사용하여 감지하는 것이 더 안전하고 정확한 방식입니다.
두 번째로, 수신 시 SCL의 posedge를 기준으로 하고, 송신 시 negedge를 기준으로 하는 이유는 I2C 프로토콜의 명세와 타이밍 요구사항에 기반합니다. 마스터는 SCL의 rising edge에서 데이터를 샘플링하고, falling edge에서 데이터를 변경합니다. 따라서 슬레이브가 데이터를 수신할 때는 rising edge(즉, posedge)에서 SDA의 값을 읽어야 하고, 데이터를 송신할 때는 falling edge(즉, negedge)에서 SDA 값을 출력해야 마스터가 그 다음 rising edge에서 안정된 값을 읽을 수 있습니다. 이처럼 수신과 송신 시점을 구분하는 것은 마스터와 슬레이브 간의 타이밍을 정확히 맞추기 위한 실무적 설계입니다.
세 번째로, 시스템 클럭에 동기화된 SCL을 사용하면서도 always 블록 내에 blocking assignment(=
)를 사용한 것에 대해 의문을 가지셨는데, 이는 실제로는 설계 스타일에 따라 판단해야 하는 부분입니다. 일반적으로 시스템 클럭에 동기화된 sequential 블록에서는 non-blocking assignment(<=
)를 사용하는 것이 권장됩니다. 그 이유는 여러 레지스터가 동시에 갱신되는 상황에서 시간상 앞선 계산 결과가 뒷부분 연산에 영향을 주는 것을 방지하고, 시뮬레이션과 합성 결과를 일관되게 유지하기 위함입니다. 하지만 질문하신 코드에서는 SCL의 posedge나 negedge를 클럭처럼 사용하는 블록들이 있어, 이들이 순차적인 레지스터 동작이 아닌 이벤트 기반 처리에 가깝기 때문에 일부는 blocking assignment로 작성되었을 가능성이 있습니다. 다만 시스템 클럭이나 동기화된 SCL을 클럭처럼 사용하는 경우에는 non-blocking assignment를 사용하는 것이 더 안정적인 설계 방식이며, 추후 유지보수나 확장성을 고려한다면 수정이 권장됩니다.
정리하면, Start/Stop 조건은 비동기적 이벤트이기 때문에 동기화하지 않고 원 신호를 사용하는 것이 정확성을 위해 필요하고, 수신과 송신 시점을 다르게 처리하는 것은 I2C 타이밍 요구사항에 부합하기 위한 설계이며, blocking assignment는 상황에 따라 사용될 수 있지만, 동기화된 클럭 기반 로직에서는 일반적으로 non-blocking assignment를 사용하는 것이 좋습니다.
강의 열심히 들어주셔서 감사드리며, 추가 질문이 있다면 언제든지 편하게 문의주세요.
안녕하세요. 추가 질문이 있어서 답글 남깁니다!
데이터를 송수신할 때, 시스템 클럭과 SCL을 동기화하는 이유를 정확히 모르겠습니다.
답변 감사합니다!