inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

디지털 회로설계 실무 : Computer Architecture 와 SoC 프로토콜 Digital IP 설계하기

CPU의 Spec.에 표기되는 L1, L2, L3 Cache 메모리 설계하기

cache관련 질문 드립니다

해결된 질문

26

서윤

작성한 질문수 11

0

제가 아무것도 없이 cache코드를 작성하기에는 실력이 부족해서 넘겨주신 자료를 보면서 한줄 한줄 해석하면서 공부를 하고 있는데

다른 수업을 하시는 강사님이 cache 코드를 보고 일단은 block으로 그려서 신호가 왜 그렇게 들어고 나가는지 왜 신호를 assign했는지 알고 언제 신호가 들어고 등을 그려보는게 공부하는데 도움이 될거라고 하는데 block으로 그리면 input output신호는 전부 코드를 보고 그리는 건데 그렇게 그리는게 정말 도움이 될까요? 아니면 회로도 그리는게 다른 방식이 있나요?

컴퓨터-구조 verilog-hdl fpga 임베디드 amba

답변 1

1

회로설계 멘토 삼코치

안녕하세요, 답변 남겨드립니다.

처음 cache 코드를 보실 때 막막한 게 아주 자연스럽습니다. cache는 Verilog 문법만 안다고 바로 읽히는 코드가 아니라, 컴퓨터 구조에서 배운 개념이 RTL 회로로 어떻게 바뀌는지를 같이 봐야 하는 부분입니다. 그래서 “block으로 그려보라”는 조언은 꽤 좋은 방향입니다. 다만 여기서 말하는 block 그림은 코드에 있는 input, output 이름을 전부 네모 박스 주변에 적어놓는 그림이 아니라, 신호가 왜 그 방향으로 흐르는지, 어떤 판단을 만들기 위해 필요한지, 어느 clock에서 의미가 생기는지를 이해하기 위한 그림에 가깝습니다.

처음에는 회로도를 멋지게 그리려고 하지 않으셔도 됩니다. cache 전체를 큰 박스 하나로 그리고, 왼쪽에는 CPU나 processor 쪽 신호를 둡니다. 예를 들면 addr, read, write, wdata 같은 요청 신호가 들어오고, rdata, ready 같은 응답 신호가 나간다고 생각하면 됩니다. 오른쪽에는 memory나 AMBA bus 쪽 신호를 둡니다. cache miss가 났을 때 외부 memory에 read 요청을 보내고, data를 받아오는 방향이라고 생각하시면 됩니다. 이렇게만 그려도 “cache는 CPU와 memory 사이에서 중간 판단을 해주는 회로구나”라는 큰 구조가 잡힙니다.

그다음에는 cache 안쪽을 조금씩 나누면 됩니다. 가장 먼저 봐야 할 부분은 address입니다. cache는 address를 그대로 쓰지 않고 보통 tag, index, offset으로 나눕니다. 예를 들어 32-bit address를 쓰고, cache line size가 32Byte라면 offset은 5bit가 됩니다. 32Byte = 2^5이기 때문에 addr[4:0]이 line 안에서 몇 번째 byte인지를 나타냅니다. cache line이 128개라면 index는 7bit가 됩니다. 그러면 addr[11:5]가 index가 되고, 나머지 addr[31:12]가 tag가 됩니다. 이걸 그림으로 한 번 나눠놓으면, 코드에서 왜 addr_index, addr_tag, addr_offset 같은 신호를 assign하는지 훨씬 잘 보입니다.

assign을 보고 헷갈릴 때는 “이 값이 저장되어야 하는 값인가, 아니면 지금 입력으로 바로 계산되는 값인가”를 기준으로 보시면 좋습니다. 예를 들어 addr에서 tag만 잘라내는 신호는 저장할 필요가 없습니다. addr_tag = addr[31:12]처럼 현재 address만 보면 바로 알 수 있으니 assign으로 만드는 게 자연스럽습니다. hit도 비슷합니다. valid bit가 1이고, 저장된 tag와 현재 address의 tag가 같으면 hit입니다. 그래서 hit = valid & (stored_tag == addr_tag) 같은 조합 논리로 만들 수 있습니다. 반대로 valid bit, dirty bit, state 같은 값은 시간이 지나도 기억하고 있어야 하므로 clock에 맞춰 저장되는 register가 필요합니다.

cache 그림을 그릴 때 처음부터 모든 신호를 다 넣으면 오히려 더 어려워집니다. 코드에 신호가 50개, 100개 있으면 그걸 전부 선으로 연결하는 순간 그림이 복잡한 거미줄처럼 됩니다. 처음에는 중요한 신호만 잡으시면 됩니다. CPU에서 들어오는 addr, read, write, wdata를 그리고, 내부에는 tag array, data array, valid bit, comparator, mux, control FSM 정도만 놓습니다. comparator는 현재 addr_tag와 tag array에서 읽은 stored_tag를 비교하는 블록입니다. mux는 hit일 때 어떤 data를 CPU로 돌려줄지 선택하는 블록입니다. control FSM은 hit인지 miss인지에 따라 다음 동작을 정하는 블록이라고 보면 됩니다.

여기서 회로 공부가 되는 지점은 “신호 이름을 외우는 것”이 아니라 “왜 그 신호가 필요한지 이해하는 것”입니다. 예를 들어 hit 신호는 CPU에게 바로 data를 줄 수 있는지를 판단하기 위해 필요합니다. miss 신호는 외부 memory에 요청을 보내야 하는지를 판단하기 위해 필요합니다. dirty bit는 기존 cache line을 그냥 버려도 되는지, 아니면 memory에 먼저 써야 하는지를 판단하기 위해 필요합니다. valid bit는 cache 안에 들어 있는 값이 진짜 유효한 데이터인지 확인하기 위해 필요합니다. 이렇게 하나씩 역할을 붙이면 코드가 훨씬 덜 낯설어집니다.

timing 그림도 꼭 같이 그려보셨으면 합니다. block 그림은 “무엇과 무엇이 연결되어 있는지”를 보여주지만, timing 그림은 “언제 값이 들어오고 언제 결과가 나가는지”를 보여줍니다. cache는 이 부분이 중요합니다. 예를 들어 CPU가 cycle N에 address를 넣었을 때, hit 결과가 같은 cycle에 바로 나오는 구조도 있고, FPGA BRAM을 쓰면 data가 cycle N+1에 나오는 구조도 많습니다. FPGA에서는 block RAM이 보통 synchronous read라서 address를 넣고 다음 clock에 data가 나옵니다. 그래서 코드에서 ready나 rdata가 한 박자 늦게 나오는 것처럼 보일 수 있습니다. 이걸 timing 그림 없이 보면 “왜 굳이 register를 한 번 더 거치지?”라고 느끼기 쉽습니다.

AMBA나 AXI 쪽이 붙어 있으면 valid와 ready를 특히 조심해서 보셔야 합니다. valid가 1이라고 해서 전송이 끝난 게 아닙니다. valid=1이고 ready=1인 clock에서만 실제 전송이 일어납니다. 예를 들어 arvalid=1인데 arready=0이면 cache가 read address를 내보내고는 있지만, bus 쪽에서 아직 받아준 것이 아닙니다. 이때 state를 다음 단계로 넘겨버리면 simulation에서는 운 좋게 지나가도 실제 연결에서는 멈추거나 data가 꼬일 수 있습니다. 실무에서도 이런 handshake 실수는 자주 나옵니다. 그래서 bus 쪽 신호는 block 그림보다 timing 그림으로 보는 편이 더 잘 이해됩니다.

공부 순서는 너무 어렵게 잡지 않으셔도 됩니다. 먼저 read hit만 보시면 됩니다. CPU가 address를 줍니다. cache는 address에서 index를 뽑습니다. 그 index로 tag array와 data array를 읽습니다. 읽어온 tag와 현재 address의 tag를 비교합니다. valid가 1이고 tag가 같으면 hit입니다. hit이면 data array에서 읽은 data 중 offset에 맞는 부분을 골라 CPU에 돌려줍니다. 이 흐름 하나만 그림으로 그려도 cache 코드의 핵심 절반은 보이기 시작합니다.

read hit이 조금 익숙해지면 write hit을 보시면 됩니다. CPU가 address와 wdata를 줍니다. cache는 같은 방식으로 index를 찾고 tag를 비교합니다. hit이면 data array의 해당 위치를 새 data로 바꿉니다. byte enable이 있으면 전체 word를 다 쓰는 게 아니라 특정 byte만 바꿀 수도 있습니다. write-back cache라면 이때 dirty bit를 1로 세웁니다. dirty bit가 1이라는 말은 “cache 안의 데이터는 바뀌었는데, 아직 memory에는 반영되지 않았다”는 뜻입니다. 이 의미를 알고 보면 dirty bit 관련 코드가 훨씬 자연스럽게 읽힙니다.

그다음에 miss를 보시면 됩니다. miss는 cache 코드에서 가장 복잡해지는 부분입니다. read miss가 나면 외부 memory에서 cache line 하나를 가져와야 합니다. line size가 32Byte라면 한 번에 32Byte를 채워야 하므로, bus width가 32bit인 경우 4Byte씩 8번 받아야 할 수 있습니다. 이때 refill counter 같은 신호가 생길 수 있습니다. write-back cache라면 기존 line이 dirty일 때 바로 refill하면 안 되고, 먼저 기존 데이터를 memory에 써야 합니다. 그래서 state가 IDLE, COMPARE, WRITEBACK, REFILL 같은 식으로 나뉘는 경우가 많습니다. state 그림을 옆에 같이 그리면 코드 흐름이 훨씬 편해집니다.

처음 배우는 입장에서는 “회로도를 어떻게 그리는 게 맞는지”보다 “내가 이 코드를 설명할 수 있는 그림인지”가 더 중요합니다. 멋진 툴로 그릴 필요도 없습니다. 종이에 네모 몇 개 그리고 화살표를 긋는 정도면 충분합니다. tag array 옆에는 index로 읽는다고 적고, comparator 옆에는 stored_tag == addr_tag라고 적고, data mux 옆에는 offset으로 선택한다고 적으면 됩니다. 수식도 복잡하게 쓰지 말고 hit = valid & tag_match 정도로만 적어도 좋습니다.

코드를 한 줄씩 해석하는 공부도 의미는 있습니다. 다만 cache처럼 구조가 있는 RTL은 한 줄 해석만으로는 전체 그림이 잘 안 잡힙니다. 예를 들어 assign miss = req_valid & ~hit 같은 한 줄은 문법적으로는 쉽지만, 이 miss가 control FSM으로 들어가고, memory request를 만들고, CPU ready를 낮추는 흐름까지 봐야 진짜 이해가 됩니다. 그래서 한 줄 해석을 하시되, 해석한 신호를 block 그림에 하나씩 붙여보는 방식이 좋습니다. 그러면 “이 신호는 comparator의 출력이구나”, “이 신호는 FSM으로 들어가는 조건이구나”, “이 신호는 CPU를 stall시키는 데 쓰이는구나”처럼 위치가 잡힙니다.

실무에서 RTL을 읽을 때도 비슷하게 봅니다. 처음 보는 cache 코드를 받으면 바로 모든 always문을 세세하게 읽기보다, CPU interface가 어떤 protocol인지, memory interface가 어떤 protocol인지, hit latency가 몇 cycle인지, miss penalty가 어느 정도인지, dirty 처리 방식이 있는지부터 봅니다. 예를 들어 hit latency가 1 cycle인지 2 cycle인지에 따라 CPU stall 설계가 달라지고, miss refill이 8 beat인지 16 beat인지에 따라 성능이 달라집니다. 100MHz에서 돌아가는 과제용 cache와 300MHz 이상을 목표로 하는 제품용 cache는 register를 넣는 위치도 달라질 수 있습니다. 같은 기능이라도 timing margin을 10~20% 확보하려면 조합 경로를 짧게 자르는 설계가 필요합니다.

질문하신 상황에서는 block으로 그리는 공부를 해보시는 게 맞습니다. 단, 처음부터 완벽한 회로도를 그리려고 하면 부담이 너무 큽니다. 오늘은 read hit 경로만 그린다, 다음에는 write hit 경로만 그린다, 그다음에는 read miss refill 경로만 그린다처럼 나눠서 접근하시면 됩니다. 각 그림에는 신호를 너무 많이 넣지 말고, 처음에는 10개 정도만 넣어도 충분합니다. addr, tag, index, offset, valid, stored_tag, tag_match, hit, data_out, ready 정도만 제대로 이해해도 cache 코드가 갑자기 훨씬 읽히기 시작할 겁니다.

이 공부를 하다 보면 어느 순간 Verilog 코드가 그냥 문장이 아니라 회로처럼 보이기 시작합니다. assign은 조합 논리로 보이고, always_ff는 register 묶음으로 보이고, case문은 FSM 상태 전이로 보이게 됩니다. 그때부터는 코드 한 줄을 읽어도 “아, 이건 mux select를 만드는 거구나”, “이건 miss 동안 CPU 요청을 잡아두는 register구나”, “이건 AXI ready가 안 왔을 때 state를 유지하려는 거구나” 하고 감이 옵니다. cache는 처음 진입 장벽이 높은 주제지만, block diagram과 timing diagram을 같이 그리면서 보면 분명히 따라갈 수 있는 구조입니다.

강의 만료일 연장 신청

0

55

2

강의자료 pdf파일

0

108

2

수강기간변경

0

83

2

프로그램 종류

0

104

1

안녕하세요 강의 도중 궁금한 점 있어서 질문드립니다!

0

96

1

수강기간 변경관련

0

70

2

수강기간 만료

0

75

2

수강 연장 문의

0

77

2

I2C SCL_Synched, SDA_Synched 질문

0

100

1

no data path질문

0

114

3

SRAM 강의 modelsim 시뮬레이션에서 inner 메모리를 Objects 리스트에서 찾으려고 하는데 안보입니다.

0

102

2

DataMem 스펙 질문

0

63

1

Hamming Code 질문

0

82

1

W_CRC 값 질문하기

0

70

1

Precharge에 대한 질문

0

188

1

tpsram spec 질문

0

75

2

학습 관련 질문 있습니다!

0

97

2

[과제7] AXI 통신 프로토콜 설계하기 문의드립니다

0

106

2

SRAM 강의 TB 작성 후 waveform 확인시에 dout이 모두 don't care 처리 관련 질문드립니다.

0

118

5

SRAM Interface Behavior(Diagram) 질문

0

152

3

vivado와 quartus 프로그램의 systhesis 결과 차이에 대해 궁금한 점 있습니다

0

196

2

tool 질문

0

102

2

CDC 메타스태빌리티 질문

0

77

1

SRAM module RTL viewer 이상

0

105

2