<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Texas_DV_engineer</title>
        <link>https://velog.io/</link>
        <description>Design Verification engineer</description>
        <lastBuildDate>Sun, 31 May 2026 21:06:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Texas_DV_engineer</title>
            <url>https://velog.velcdn.com/images/houston_guy2/profile/d90acedc-a60f-469f-8221-09efa3e93415/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Texas_DV_engineer. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/houston_guy2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[CH2: Digital Logic and Hardware Fundamental (14~23)]]></title>
            <link>https://velog.io/@houston_guy2/CH2-Digital-Logic-and-Hardware-Fundamental-1423</link>
            <guid>https://velog.io/@houston_guy2/CH2-Digital-Logic-and-Hardware-Fundamental-1423</guid>
            <pubDate>Sun, 31 May 2026 21:06:43 GMT</pubDate>
            <description><![CDATA[<h2 id="14-what-is-the-difference-between-combinational-and-sequential-logic">14. What is the difference between Combinational and Sequential logic?</h2>
<ul>
<li>Combinational logic: Output depends only on the current inputs. There is no memory.(AND gates, Multiplexer, adders)</li>
<li>Sequential logic: Output depends on the current inputs and the previous state(stored in flip-flops or latches) (counters, FSM)</li>
</ul>
<pre><code class="language-c">always_comb begin
    y = a &amp; b;
end

always_ff @(poesedge clk or negedge rst_n) begin
    if (!rst_n) q &lt;= 1&#39;b0;
    else q &lt;= d;
end</code></pre>
<h3 id="follow-up-q-how-does-a-verification-engineer-distinguish-between-the-two-in-rtl">Follow-up Q: How does a verification engineer distinguish between the two in RTL?</h3>
<p>Combinational logic is modeled using always_comb or continuous assign statements. Sequential logic is modeled using always_ff @(posedge clk). if a designer accidentally infers a latch inside always_comb, it becomes sequential- a common bug to watch for.</p>
<h2 id="15-what-is-a-multiplexermux-how-do-you-build-a-321-mux-from-21-muxes">15. What is a Multiplexer(Mux)? How do you build a 32:1 mux from 2:1 muxes?</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f461d5a0-4e2a-4a22-a901-7cbf5239028e/image.png" alt=""></p>
<p>A multitplexer selects one of N input signals and routes it to the output based on select signal.</p>
<pre><code class="language-c">// 2:1 mux
assign y = sel? b:a;

//4:1 mux using nested 2:1 muxes
assign m0 = sel[0] ? in[1]:in[0];
assign m1 = sel[0] ? in[3]:in[2];
assign y = sel[1] m1 : m0;</code></pre>
<p>Building a 32 mux from 2:1 muxes:
32:1 mux requires 5 select bits ($2^5$=32). It is built as a tree of 2:1 muxes in 5 levels.</p>
<h3 id="follow-up-q-what-is-a--mux-tree-and-why-does-depth-matter">Follow-up Q: What is a &quot; mux tree&quot; and why does depth matter?</h3>
<p>Each level adds propagation delay. A 32:1 Mux has 5 levels of delay. Deep mux trees can become the critical path in timing closure. In high-speed designs, pipeline registers may be inserted between levels.</p>
<h2 id="16-what-is-a-decoder-what-is-the-encoder-what-is-a-priority-encoder">16. What is a Decoder? What is the Encoder? What is a Priority Encoder?</h2>
<table>
<thead>
<tr>
<th>Block</th>
<th>Function</th>
<th>Input-output</th>
<th>Example Use</th>
</tr>
</thead>
<tbody><tr>
<td>Decoder</td>
<td>Converts N-bit binary to $2^N$ one-hot outputs</td>
<td>3 bit-&gt; 8 lines</td>
<td>Address decoding, memory chip select</td>
</tr>
<tr>
<td>Encoder</td>
<td>Converts $2^N$ one-hot inputs to N-bit binary</td>
<td>8 lines -&gt; 3 bit</td>
<td>Interrupt encoding</td>
</tr>
<tr>
<td>Priority Encoder</td>
<td>Like an encoder, but resolves conflicts when multiple inputs are active simultaneously</td>
<td>8 lines -&gt; 3 bit + valid</td>
<td>Interrupt priority, arbiter logic</td>
</tr>
</tbody></table>
<pre><code class="language-c">// 3 to 8 Decoder
always_comb begin
    decode = 8&#39;b0;
    decoe[addr] = 1&#39;b1;
end

// Priority Encoder
always_comb begin
    casez (request)
        8&#39;b1???????: grant = 3&#39;d7;
        8&#39;b01??????: grant = 3&#39;d6;
        8&#39;b001?????: grant = 3&#39;d5;
        8&#39;b0001????: grant = 3&#39;d4;
        8&#39;b00001???: grant = 3&#39;d3;
        8&#39;b000001??: grant = 3&#39;d2;
        8&#39;b0000001?: grant = 3&#39;d1;
        8&#39;b00000001: grant = 3&#39;d0;
        default:     grant = 3&#39;d0;
    endcase
end</code></pre>
<h3 id="follow-up-q-why-is-casez-used-instead-of-case-in-priority-encoder">Follow up Q: Why is casez used instead of case in priority encoder?</h3>
<p>casez treats &quot;?&quot; as &quot;don&#39;t care bits&quot;, allowing you to match patterens where lower-priority bits irrelevant. Using case would require listing every possible combination explicitly. </p>
<h2 id="18-in-rtl-design-why-are-latches-generally-avoided-while-flip-flops-are-ubiquitous">18. In RTL design, Why are Latches generally avoided while Flip-Flops are ubiquitous?</h2>
<ul>
<li>Latches are level sensitive: They pass data transparently as long as the enable signal is active. This timing analysis extremely difficult because data can ripple through multiple latches in a single clock phase, creating unpredicatable critical paths</li>
<li>Flip-Flops are edge-triggered: They only sample data on the rising (or falling) edge of a clock. This enforces strict boundaries. Data must arrive before the clock edge(Setup timem) and remain stable slightly afer (Hold time). This makes Static Timing Anaysis(STA) predictable and robust.</li>
</ul>
<p>Trap: Are latches always bad?:
No, they are heavily used in custom datapath design, clock gating cells to save power. However they should never be inffered accidentally by a missing &quot;else&quot; statement in a  combinational &quot;always&quot; block.</p>
<h3 id="follow-up-how-does-a-latch-get-accidentally-inferred-in-rtl">Follow up: How does a latch get accidentally inferred in RTL?</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[CH 1: Introduction to Modern Verification]]></title>
            <link>https://velog.io/@houston_guy2/CH-1-Introduction-to-Modern-Verification</link>
            <guid>https://velog.io/@houston_guy2/CH-1-Introduction-to-Modern-Verification</guid>
            <pubDate>Sun, 31 May 2026 20:51:43 GMT</pubDate>
            <description><![CDATA[<h2 id="1-what-is-the-verification-gap-why-does-verification-consume-70-of-the-asic-development-asic-development-cycle">1. What is the verification gap? why does verification consume 70% of the ASIC development ASIC development cycle?</h2>
<p>The verification gap is the disparity between design complexity and verification productivity.</p>
<p>verifiation consumes up to 70% of the cycle because it is fundamentally an ubbounded problem.</p>
<p>Designers build one specific path of intend funcionality. Verification engineers must prove that all other infinite paths do not result in catarophic failures. Finding bug is easy, proving the absence of bug is mathmatically impossible for large designs. </p>
<h2 id="2-contrast-directed-testing-with-constrained-random-verificationcrv-when-does-crv-fail">2. Contrast Directed Testing with Constrained Random Verification(CRV). When does CRV fail?</h2>
<table>
<thead>
<tr>
<th></th>
<th>Directed Testing</th>
<th>CRV</th>
</tr>
</thead>
<tbody><tr>
<td>Approach</td>
<td>Engineer manually writes stimulus for specific senarios</td>
<td>Testbench generates randomized stimulus</td>
</tr>
<tr>
<td>Effort</td>
<td>Low setup, High test-writing effort</td>
<td>High setup, low test writing effort</td>
</tr>
</tbody></table>
<p>When CRV fails: CRV is inefficient at hitting highly specific, deep-state corner case. 
Solution: In these case, engineers use directd-random tests or Formal Verification</p>
<h2 id="3-what-is-formal-verification-how-does-it-differ-from-simulation">3. What is Formal Verification? How does it differ from Simulation?</h2>
<p>Formal Verification uses mathematical proofs(model checking) rather than simulation logic</p>
<p>Simulation: Injects specific vectors(00, 01, 11) and checks the output. it can easily miss a bug if that exact vector sequence wasn&#39;t generated</p>
<p>Formal: Asserts that &quot;output Must equal A+B&quot; The formal solver exploers the entire mathmatical state space simultaneously.</p>
<h4 id="follow-up-q-can-formal-verification-completely-replace-simulation">Follow up Q: Can Formal Verification completely replace simulation?</h4>
<p>No, Formal works best on control- heavy logic with a bounned state space. Data path heavy blocks and full SoC intergration still require simulation-based CRV. </p>
<h2 id="4-the-verification-lifecycle">4. The verification lifecycle</h2>
<ol>
<li>Specification Review</li>
<li>Verification plan</li>
<li>environment Bring-up<ul>
<li>Developing the UVM agents, scoreboards and virtual sequene</li>
</ul>
</li>
<li>Sanity/Smoke testing <ul>
<li>Running basic directed test(reset test, register read/write) to ensure the DUT is alive</li>
</ul>
</li>
<li>Feature Extraction(CRV)<ul>
<li>unleashing crv test </li>
</ul>
</li>
<li>Coverage Closure<ul>
<li>Anlying coverage holes</li>
</ul>
</li>
<li>Gate-Level Simulation(GLS) <ul>
<li>Running the testbench against the synthesized netlist</li>
</ul>
</li>
<li>Tapeput/Sign-off</li>
</ol>
<h2 id="5-what-makes-a-good-verification-plan-vplan-what-should-it-contain">5. What makes a good Verification Plan (vPlan)? What should it contain?</h2>
<p>A vPlan is the contract between verification and design. A poor vPlan leads to missing features and silicon bugs. A good vPlan is extractable, measurable, and maps directly to coverage. </p>
<p>Feature Description
Stimulus Strategy
Checking Strategy
Coverage Mapping</p>
<h2 id="6-describe-the-lifecyle-of-a-bug-what-details-are-crucial-when-filing-a-jirabugzilla-ticket">6. Describe the lifecyle of a bug. What details are crucial when filing a JIRA/Bugzilla ticket?</h2>
<p>New -&gt; assigned -&gt; in progress -&gt; resolved(code fix) -&gt; verified(test pass) -&gt; closed</p>
<p>Title
Reproduction steps
Log/Waveform
Root cause analysis
Severity/priority</p>
<h2 id="7-what-is-a-continuous-intergrationci-and-regression-framework">7. What is a Continuous Intergration(CI) and Regression framework?</h2>
<p>CI is the automated process of running verfication checks every time a designer commits new RTL code. </p>
<p>Sanity regression: fast 30min suite of tests on every pull request
Nightly Regression: massive suite of thousands of randomized tests
Weekly Regression: slow, exhaustive test like Gate level simulation</p>
<h2 id="8-what-is-the-difference-simulation-emulation-and-fpga-prototyping">8. What is the difference Simulation, Emulation, and FPGA prototyping?</h2>
<table>
<thead>
<tr>
<th></th>
<th>Simulation</th>
<th>Emulation</th>
<th>FPGA protyping</th>
</tr>
</thead>
<tbody><tr>
<td>speed</td>
<td>10-100 HZ</td>
<td>1-5 MHz</td>
<td>10-50MHz</td>
</tr>
<tr>
<td>Visibility</td>
<td>100%</td>
<td>Moderate</td>
<td>Low</td>
</tr>
<tr>
<td>Compile time</td>
<td>Minutes</td>
<td>Hours to Days</td>
<td>Day to weeks</td>
</tr>
<tr>
<td>primary use</td>
<td>RTL function bug hunting</td>
<td>OS booting, Firmware development, IP intergration</td>
<td>Real-world interface testing</td>
</tr>
</tbody></table>
<p>Why we need Emulation:booting an OS like linux on an SoC via RTL simulation sould take years of real world time. Emulation runs it in hours</p>
<h2 id="9-what-happens-when-a-bug-escapes-into-silicon-post-silicon-validation">9. What happens when a bug escapes into Silicon? (Post-Silicon Validation)</h2>
<p>A silicon bug is the worst case scenario. if a bug escapes the front-end verification team:</p>
<ol>
<li>Silicon Bring-up</li>
<li>Failure Detected</li>
<li>Reproduction</li>
<li>Metal Spin / FIB</li>
<li>Coverage Hole Analysis</li>
</ol>
<h2 id="10-a-test-in-the-nightly-regrssion-failed-randomly-when-you-rerun-the-exact-same-test-with-the-exact-same-seed-on-your-local-machine-it-passes-what-are-the-potential-causes">10. A test in the nightly regrssion failed randomly. When you rerun the exact same test with the exact same seed on your local machine, it passes. What are the potential causes?</h2>
<p>This is a calssic &quot;non=deterministic&quot; failure. causes include:</p>
<ol>
<li>uninitialized variables</li>
</ol>
<ul>
<li>A variable or register dosen&#39;t have a reset alue. on the server farm, simulator memory allocated it as 1, causing failure. on your machine, it allocated as 0 passing by luck. </li>
</ul>
<ol start="2">
<li>Race conditions</li>
</ol>
<ul>
<li>Two initial blocks or always blocks are executing in the same tiem-step delta cycle. different server architectures or simulator loads might schedule them in a different order.</li>
</ul>
<ol start="3">
<li>File/OS dependencies</li>
</ol>
<ul>
<li>The test on the server might be picking up a different version of a shared library, DPI-C code, or reading an uninitialized file.</li>
</ul>
<ol start="4">
<li>Tool Version Mismatch</li>
</ol>
<h2 id="11-you-join-a-team-and-coverage-dashboard-shows-100-code-coverage-and-100-functional-coverage-is-the-design-bug-free">11. You join a team and coverage dashboard shows 100% Code coverage and 100% Functional Coverage. Is the design bug free?</h2>
<p>Absolutely not</p>
<ul>
<li>100% Code Coverage merely means every line of RTL was executed. It does not mean the RTL did the correct thing. </li>
<li>100% Functional Coverage means the testbench exercised everything specified in the verification plan. </li>
</ul>
<p><strong><em>If the Verification Plan is missing a feature</em></strong>, or if a specific combination of concurrent events wasn&#39;t defined as a cros-coverage bin, <em><strong>a critical bug can easily hide behind 100% coverage numbers</strong></em>. Coverage is only as good as the imagination of the engineer who wrote the bounds. </p>
<h2 id="12-what-is-a-systematic-approach-to-debugging-a-failing-test">12. What is a systematic approach to debugging a failing test?</h2>
<p>Effective debugging follows a structured process rather than trial and error:</p>
<ol>
<li>Isolate</li>
</ol>
<ul>
<li>Determine if the failure is in the DUT, the testbench, or the test stimulus.</li>
</ul>
<ol start="2">
<li>Reproduce<ul>
<li>Find the Exact seed and the minimum test configuration that triggeers the failure consistently</li>
</ul>
</li>
<li>Localize</li>
</ol>
<ul>
<li>Use $display, waveform dumps, or assertions to find the exact clock cycle</li>
</ul>
<ol start="4">
<li>Hypothesize &amp; Prove</li>
</ol>
<ul>
<li>Form a theory and add a targeted. assertion</li>
</ul>
<ol start="5">
<li>Root Cause Analysis</li>
</ol>
<ul>
<li>Determine whether the bug is in the RTL oor if the sppecifiation itself is ambiguous</li>
</ul>
<ol start="6">
<li>Fix &amp; Regress</li>
</ol>
<ul>
<li>Apply the fix and run a targeted regreession to ensure no new failures are introduced</li>
</ul>
<h2 id="13-how-are-ai-and-llms-being-used-in-verification-today">13. How are AI and LLMs being used in verification today?</h2>
<p>AI tools are increasingly used as productiviy aids in verification work-flows</p>
<ul>
<li>Code generation: LLMs can generate boilerplate UVM components and automation scripts</li>
<li>Coverage Analysis: AI-driven tools can analyze coverage holes and suggest constraint modifictions to reach uncovered bins.</li>
<li>Legacy Code Understanding: LLMs help engineers quickly understand unfamilirar or poorly documented legacy testbench.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[week 5,6]]></title>
            <link>https://velog.io/@houston_guy2/week-56</link>
            <guid>https://velog.io/@houston_guy2/week-56</guid>
            <pubDate>Wed, 27 May 2026 23:34:20 GMT</pubDate>
            <description><![CDATA[<ol>
<li>검증 대상 (DUT: Design Under Test)
기능: CPU의 요청(Read/Write)을 처리하는 2-Way Set Associative L1 캐시.</li>
</ol>
<p>핵심 동작: * Hit: 캐시에 데이터가 있으면 즉시 응답 (Performance).
Miss: 캐시에 없으면 DRAM에서 데이터를 가져옴 (Allocate).
Eviction: 캐시가 꽉 찼을 때, 기존 데이터를 DRAM으로 쫓아냄 (Write-Back).</p>
<p>우리가 만든 스코어보드의 Match 조건:
읽기(Read, RW=0): CPU가 데이터를 읽으려 할 때, 캐시에 있는 데이터가 정답지(ref_mem)와 똑같으면 Match++
쓰기(Write, RW=1): CPU가 데이터를 쓸 때, 메모리로 데이터를 쫓아내는(Eviction) 과정에서, 쫓겨난 데이터가 정답지와 똑같으면 Match++</p>
<ol start="2">
<li><p>검증 환경 (UVM Verification Environment)
Agent: CPU/Memory 역할을 하는 에이전트를 구축하여 트랜잭션 주입 및 응답 모니터링.
Scoreboard: Ref Model(참조 모델)과 비교하여 데이터 무결성(Data Integrity) 실시간 검증.
Sequence: 단순한 시퀀스부터 랜덤 융단폭격(500회)까지 수행하여 에지 케이스(Edge case) 탐색.</p>
</li>
<li><p>핵심 트러블슈팅 (핵심 실력 검증 구간)
우리가 이 프로젝트에서 직접 해결한 하드웨어 결함들은 다음과 같습니다.</p>
</li>
</ol>
<p>Issue 1: X-State 블랙홀 버그
원인: 초기화되지 않은 메모리(X)를 캐시가 유효한 데이터로 착각하여 잘못된 로직으로 진입.
해결: === 연산자를 활용한 엄격한 Miss 판별 로직(방어막) 적용.</p>
<p>Issue 2: SRAM Write-Read Collision (충돌)
원인: 같은 클럭에 데이터 쓰기와 읽기가 겹치면서 데이터가 증발함.
해결: 포워딩 패스(Bypass) 로직을 RTL에 직접 설계하여 충돌 시 안전한 데이터를 즉시 전달.</p>
<p>Issue 3: 어드레스 정렬(Alignment) 미스
원인: CPU가 무작위 주소를 쐈으나 캐시는 블록 단위(16바이트)로 정렬된 주소만 처리함.
해결: 비트 마스킹(&amp; 32&#39;hFFFF_FFF0)을 통해 주소를 캐시 규칙에 맞게 정렬하여 테스트.</p>
<ol start="4">
<li>최종 성적표 (결과)
데이터 무결성: 500회 랜덤 테스트 동안 Mismatch 0 기록 (무결점 입증).
커버리지: 2-Way 캐시의 모든 동작(RW 조합, 인덱스 구간)에 대해 100% 테스트 달</li>
</ol>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/fba65d9e-7ad0-48d7-a811-56b592ecc8cb/image.png" alt=""></p>
<p>중요한 핵심은 바로 이 한 줄입니다:
UVM_ERROR ... [RAW_HAZARD_BUG] [FAIL] Read Addr=0x000010bd: Exp=0x1e6281c9, Act=0x00000000</p>
<p>이 로그가 말해주는 스토리는 다음과 같습니다.</p>
<p>CPU의 명령: &quot;야 캐시, 0x000010bd 주소에 0x1e6281c9라는 데이터를 써(Write)!&quot;</p>
<p>CPU의 얌체짓 (B2B): 그리고 바로 다음 클럭에 숨 쉴 틈도 없이 &quot;방금 쓴 0x000010bd 주소 데이터 다시 읽어줘(Read)!&quot; 라고 명령했습니다.</p>
<p>스코어보드(Exp): &quot;방금 썼으니까 당연히 방금 쓴 값(0x1e6281c9)이 나와야지.&quot;</p>
<p>캐시의 실패(Act): 하지만 우리의 파이프라인 캐시는 데이터를 메모리에 미처 다 적기도 전에 읽기 명령이 들어오자, 아직 업데이트되지 않은 과거의 쓰레기 값(0x00000000)을 뱉어버렸습니다.</p>
<p>이것이 바로 그 유명한 RAW(Read-After-Write) Data Hazard입니다. 승윤님의 테스트벤치가 이 치명적인 약점을 정확하게 찔러서 에러(MISMATCH=1)를 띄운 것입니다.</p>
<h2 id="week-7">week 7</h2>
<p>b2b seq
<img src="https://velog.velcdn.com/images/houston_guy2/post/0f3c0052-bd98-4c59-b805-d9d4430d263c/image.png" alt=""></p>
<p>5개
<img src="https://velog.velcdn.com/images/houston_guy2/post/d659fb86-24a0-46de-a195-215822221e4c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/4f5b328c-a1ae-4d5f-892f-53daecee83a2/image.png" alt=""></p>
<p>12개
<img src="https://velog.velcdn.com/images/houston_guy2/post/697d561e-8403-4be7-90ca-cc83b85c18a8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b538d509-aca6-4a91-b7a8-841c284e9505/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/20fc70e2-ab58-4f1f-83b6-13e2f2bbccc2/image.png" alt=""></p>
<p>이 하드웨어(RTL)는 하나의 인덱스(Set)에 2개의 데이터를 담을 수 있는 &#39;2-Way Set Associative 캐시&#39;였던 것입니다!</p>
<p>1번째 쓰기 (10a0): &quot;Way 0이 비었네? 쏙<del>&quot; (쫓겨나는 데이터 없음)
2번째 쓰기 (20a0): &quot;Way 1이 비었네? 쏙</del>&quot; (쫓겨나는 데이터 없음)
3번째 쓰기 (30a0): &quot;빈칸이 없네! 제일 오래된 10a0 방 빼!&quot; (축출 1 발생 ➡️ Match 1)</p>
<p>... (반복) ...</p>
<p>12번째 쓰기 (c0a0): &quot;빈칸 없네! a0a0 방 빼!&quot; (축출 10 발생 ➡️ Match 10)</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/eee009a3-5281-4cd5-800c-b46ea4a20c36/image.png" alt=""></p>
<ol>
<li><p>드디어 터진 [WRITE_BACK_PASS]</p>
<p>UVM_INFO ... [WRITE_BACK_PASS] Perfectly evicted!</p>
</li>
</ol>
<p>우리가 방금 수술했던 &#39;Dirty Bit&#39; 로직이 완벽하게 작동했다는 뜻입니다! 캐시가 꽉 차서(Miss 발생 시) 기존 데이터를 메모리로 쫓아낼 때(Eviction), 쫓겨나는 데이터의 값이 스코어보드가 예상한 정답(Ref Model)과 100% 일치했다는 뜻입니다. 승윤님이 고친 RTL 코드가 하드웨어에 생명을 불어넣었습니다.</p>
<ol start="2">
<li>압도적인 성과: MATCH = 499
500번의 데이터 폭격을 가했는데, 캐시 안에서 치고받고 싸우다가 쫓겨난 데이터가 무려 499개입니다. 그리고 그 499번의 방 빼기 과정에서 데이터가 단 1비트도 깨지지 않고 무사히 메모리로 전달되었습니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/e0a7e17d-f0b3-4060-a8c5-65ab704220e2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/fe10871d-63fd-4c3d-ac43-e7c2c34c5c56/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/52068905-06b6-47ea-b534-acbe24294243/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5 Essential Question]]></title>
            <link>https://velog.io/@houston_guy2/CDC-Coherency-AXI-Deadlock</link>
            <guid>https://velog.io/@houston_guy2/CDC-Coherency-AXI-Deadlock</guid>
            <pubDate>Wed, 20 May 2026 00:11:39 GMT</pubDate>
            <description><![CDATA[<h1 id="index">Index</h1>
<ol>
<li>CDC coherency</li>
<li>AXI Deadlock</li>
<li>Implicit vs Explicit Prediction</li>
<li>Vacuous Assertions</li>
<li>Constraint Bipolarity</li>
</ol>
<h1 id="basic-word">Basic word</h1>
<h2 id="metastability">Metastability</h2>
<p>Metastability in VLSI is a phenomenon where a flip-flop enters an unstable intermediate voltage state (neither stable &#39;0&#39; nor &#39;1&#39;) due to setup or hold time violations</p>
<h2 id="2ff-synchronizer">2FF Synchronizer</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/05b92add-a07a-431c-b426-9b7125760b21/image.png" alt=""></p>
<p>We use a 2-flop synchronizer to safely pass a 1-bit control signal across asynchronous clock domains.
If the input violates setup or hold time, the first flip-flop might enter a metastable state.
However, the one-clock-cycle delay between the two flops provides enough &#39;Resolution Time&#39; for the signal to settle to a stable 0 or 1, which drastically improves the system&#39;s MTBF</p>
<p>Resolution Time: 2개의 플립플롭이 만들어내는 해결책 (시간 벌기)
MTBF: 신뢰성을 증명하는 결과 지표 (Mean Time Between Failures)</p>
<hr>
<h1 id="1-cdc-coherency">1. CDC Coherency</h1>
<h3 id="a-what-is-cdc-coherency">A. What is CDC Coherency?</h3>
<p><span style="color:lightgreen">CDC Coherency means ensuring that all bits of a multi-bit signal arrive and are sampled together when crossing clock domains</span></p>
<h3 id="b-the-problem-why-simple-synchronizers-fail">B. The Problem: Why Simple Synchronizers Fail</h3>
<p><span style="color:lightgreen">If we use simple synchronizers on a data bus, routing skew will cause &#39;Data Tearing,&#39; meaning the receiving domain might sample invalid garbage values</span></p>
<ul>
<li><p>Routing Skew: Inside the chip, the physical wires for each bit have slightly different lengths. Some bits travel faster, and some travel slower.</p>
</li>
<li><p>Asynchronous Sampling: Because clk_A and clk_B are not synchronized, clk_B might sample the data exactly while it is changing.</p>
</li>
</ul>
<p>The Phantom State (Example):
If the data transitions from 4&#39;b0000 to 4&#39;b1111, clk_B might sample it right in the middle. Because of routing skew, it might read 4&#39;b0011—a garbage value (phantom state) that was never actually sent. This causes critical system failures.</p>
<h3 id="c-industry-solutions-how-to-fix-it">C. Industry Solutions (How to fix it)</h3>
<p><span style="color:lightgreen">To maintain coherency, we never synchronize the data directly. Instead, we use Asynchronous FIFOs, Gray code pointers, or Data-path handshakes.</span></p>
<h4 id="span-stylecolorlightbluegray-code-encodingspan"><span style="color:lightblue">Gray Code Encoding</span></h4>
<p>Data is encoded so that only one bit changes at a time (e.g., 00 -&gt; 01 -&gt; 11). Because only a single bit is transitioning, you will never sample a garbage value. This is strictly used for Asynchronous FIFO Pointers.</p>
<h4 id="span-stylecolorlightbluedata-path-synchronization-handshakespan"><span style="color:lightblue">Data-path Synchronization (Handshake)</span></h4>
<p>Do not put synchronizers on the data bus itself. Instead, hold the multi-bit data steady so it is physically stable. Then, pass a single 1-bit Valid (or Enable) signal through a 2-flop synchronizer. The destination domain only reads the data when the Valid signal is safely received.</p>
<h4 id="span-stylecolorlightblue-asynchronous-fifospan"><span style="color:lightblue"> Asynchronous FIFO</span></h4>
<p>For heavy, continuous traffic (like an AXI data payload), handshake is too slow. We use an Asynchronous FIFO, which safely transfers data using Dual-port RAM and Gray-coded read/write pointers.</p>
<hr>
<h1 id="2-axi-deadlock">2. AXI Deadlock</h1>
<h2 id="a-the-definition">A. The Definition</h2>
<p><span style="color:lightgreen">An AXI deadlock is a critical bug where bus traffic permanently freezes due to a circular dependency</span>(순환 의존성)</p>
<p>Basically, they wait for each other.
Master waits for READY, Slave waits for VALID. The bus just hangs with both signals stuck at zero</p>
<h2 id="b-the-causes">B. The Causes</h2>
<ol>
<li><p>Usually, it&#39;s a protocol violation. The Master&#39;s VALID should never wait for the READY signal.</p>
</li>
<li><p>On a larger SoC scale, it’s a structural issue. For example, a FIFO overflow freezes the interconnect</p>
</li>
</ol>
<h2 id="c-how-to-catch-it">C. How to Catch It</h2>
<p>To catch them in UVM environment, I need to implement <span style="color:lightgreen">SystemVerilog Assertions (SVA)</span> to monitor handshake rules and build<span style="color:lightgreen"> Watchdog Timers </span>in the Monitor to detect transaction timeouts.&quot;</p>
<hr>
<h1 id="3-implicit-vs-explicit-prediction">3. Implicit vs Explicit Prediction</h1>
<p>when the RAL mirror goes stale</p>
<h2 id="a-the-stale-mirror">A. The Stale Mirror</h2>
<p><span style="color:lightgreen">A stale mirror happens when the UVM RAL database goes out of sync with the actual hardware registers.</span></p>
<h3 id="ral-mirror란-무엇인가">RAL Mirror란 무엇인가?</h3>
<p>UVM RAL 모델은 내부에 칩의 모든 레지스터 값을 복사해 둔 가상의 메모리 공간을 가집니다. 이를 Mirror (거울)라고 부릅니다.
검증 환경은 시뮬레이션 속도를 높이기 위해, 매번 AXI 버스를 타고 물리적 하드웨어(DUT)의 값을 읽어오는 대신 이 가상의 Mirror 값을 확인(get())하여 로직을 처리합니다.</p>
<h3 id="문제-발생-the-stale-mirror">문제 발생 (The Stale Mirror):</h3>
<p>이 Mirror 값과 실제 DUT 하드웨어의 값이 달라지는 순간, 우리는 이것을 &quot;Mirror goes stale(미러 값이 썩었다/유효하지 않다)&quot;라고 표현합니다. 이때부터 테스트벤치는 엉뚱한 값을 바탕으로 채점을 하게 되어 시뮬레이션 전체가 망가집니다.</p>
<p>이 Mirror 값을 DUT와 똑같이 최신 상태로 갱신해 주는 작업이 바로 Prediction(예측)입니다.</p>
<h2 id="b-implicit-vs-explicit">B. Implicit vs. Explicit</h2>
<p>In the industry, we never use Implicit Prediction because it <span style="color:lightgreen">blindly assumes a write was successful.</span>
Instead, we strictly use Explicit Prediction. It uses the AXI Monitor and a Predictor to update the mirror only after <span style="color:lightgreen">verifying the physical bus transaction.</span></p>
<h3 id="implicit-prediction">Implicit Prediction</h3>
<p>UVM이 기본적으로 제공하는 가장 단순한 동기화 방식입니다.</p>
<p>동작 방식: 테스트벤치(Sequence)에서 reg.write(data) 명령을 내리는 바로 그 순간, UVM은 &quot;내가 데이터를 썼으니까 하드웨어에도 당연히 그 값이 써졌겠지?&quot;라고 스스로 가정하고 즉시 Mirror 값을 업데이트해 버립니다.</p>
<p>치명적인 단점 (현업에서 금지하는 이유):
만약 AXI 버스 중간에서 에러가 발생해서 하드웨어에 값이 제대로 써지지 않았다면 어떻게 될까요? 하드웨어는 이전 값을 유지하고 있는데, RAL Mirror는 이미 새 값으로 업데이트되어 버립니다. (Stale 상태 발생)
또한, 다른 마스터(예: 칩 내부의 다른 CPU)가 해당 레지스터를 건드리는 경우, UVM은 이를 전혀 눈치채지 못합니다.</p>
<h3 id="explicit-prediction-명시적-예측">Explicit Prediction (명시적 예측)</h3>
<p>The Industry Standard
현업에서는 Implicit Prediction을 기능적으로 꺼버리고(set_auto_predict(0)), 무조건 Explicit Prediction을 아키텍처에 구현합니다.</p>
<p>동작 방식: Sequence가 reg.write()를 호출하더라도 Mirror 값은 절대 바뀌지 않습니다. 대신, 버스를 물리적으로 감시하는 Monitor가 활약합니다. Monitor가 AXI 버스에서 데이터가 정상적으로 전송되는 것을 확인(Valid/Ready Handshake)한 뒤, 그 내역을 uvm_reg_predictor라는 통역사 컴포넌트에게 보냅니다. 통역사가 이 내역을 분석하여 비로소 Mirror 값을 갱신합니다.</p>
<p>장점: 물리적 버스 트래픽을 100% 반영하므로, AXI 에러가 나거나 다른 마스터가 버스를 통해 값을 바꿀 때도 Mirror가 정확하게 동기화됩니다. &quot;Trust but verify (믿지만 확인하라)&quot; 철학입니다.</p>
<h2 id="c-the-exception">C. The Exception</h2>
<p>&quot;However, even Explicit Prediction cannot catch internal hardware changes, like a FIFO status register updating itself. For those, we must force a read or use backdoor access to update the mirror.&quot;
(하지만 Explicit Prediction조차도 FIFO 상태 레지스터가 스스로 업데이트되는 것 같은 내부 하드웨어 변화는 잡아낼 수 없습니다. 이런 경우엔 강제로 Read를 하거나 Backdoor 접근을 써서 미러를 갱신해야 합니다.)</p>
<hr>
<p>SVA(SystemVerilog Assertions)에서 이 두 연산자는 전제 조건(A)이 만족되었을 때, 결과(B)를 언제부터 검사할 것인가(Timing)를 결정하는 단 하나의 물리적인 차이점만 가집니다.</p>
<p>|-&gt; (Overlapping Implication / 겹치는 연산자)
전제 조건(A)이 참(True)이 된 바로 그 동일한 클럭 사이클(Same cycle)부터 결과(B)를 검사합니다.</p>
<p>|=&gt; (Non-overlapping Implication / 겹치지 않는 연산자)
전제 조건(A)이 참이 되면, 그 클럭은 건너뛰고 다음 클럭 사이클(Next cycle)부터 결과(B)를 검사합니다.</p>
<p>A |=&gt; B 는 A |-&gt; ##1 B 와 100% 동일합니다.
A |-&gt; ##1 B 해석: A가 발생한 그 순간(|-&gt;)을 기준으로 검사를 시작하되, 1클럭 지연(##1) 후에 B를 확인하라.</p>
<h1 id="4vacuous-assertions">4.Vacuous Assertions</h1>
<p>why a passing SVA might be hiding a bug</p>
<h2 id="a-the-definition-1">A. The Definition</h2>
<p>&quot;A vacuous pass happens when the antecedent of an implication operator is never triggered during the simulation.&quot;</p>
<p>(Vacuous(공허한) pass는 조건부 연산자의 전제 조건(ˌan(t)əˈsēd(ə)nt)이 시뮬레이션 동안 단 한 번도 트리거되지 않을 때 발생합니다.)</p>
<h2 id="b-the-danger-왜-버그를-숨기는가">B. The Danger (왜 버그를 숨기는가)</h2>
<p>&quot;It is dangerous because it gives a false sense of security. <span style="color:lightgreen">The assertion reports zero errors, but in reality, the hardware logic was never actually tested because the stimulus was missing.&quot;</span></p>
<p>(이것은 가짜 안도감을 주기 때문에 위험합니다. Assertion은 에러가 없다고 보고하지만, 실제로는 자극(Stimulus)이 누락되어 하드웨어 로직이 아예 테스트조차 되지 않은 것입니다.)</p>
<h2 id="c-the-solution">C. The Solution</h2>
<p>&quot;To prevent this, <span style="color:lightgreen">I always pair my assertions with a cover property on the antecedent.</span> We must verify the coverage report to ensure the scenario actually happened, rather than just trusting the assertion pass log.&quot;</p>
<p>(이를 막기 위해, 저는 항상 전제 조건에 대해 <strong>cover property</strong>를 쌍으로 작성합니다. 단순히 Assertion 성공 로그를 믿는 대신, 커버리지 리포트를 확인해서 해당 시나리오가 실제로 발생했는지 반드시 증명해야 합니다.)</p>
<hr>
<h1 id="5-constraint-bipolarity">5. Constraint Bipolarity</h1>
<p>사실 현업 및 공식 스펙 문서에서는 Bipolarity(양극성)라는 단어보다는 Bidirectional Nature(양방향성)라는 용어를 훨씬 더 많이 사용합니다. 일반적인 C/C++ 소프트웨어 엔지니어들이 검증(DV)으로 넘어올 때 가장 많이 겪는 혼란이자 함정입니다.</p>
<p>C언어나 파이썬 같은 절차적 프로그래밍에 익숙한 사람들은 아래의 SystemVerilog 제약 조건(Constraint) 코드를 볼 때, 위에서 아래로 흐르는 순차적인 로직으로 착각합니다.</p>
<pre><code class="language-c">class my_packet;
  rand bit a; // 1-bit (0 or 1)
  rand bit b; // 1-bit (0 or 1)

  constraint c_logic {
    (a == 1) -&gt; (b == 1);
  }
endclass</code></pre>
<p>Constraint Bipolarity (양방향성)의 진짜 의미
SystemVerilog의 제약 조건은 왼쪽에서 오른쪽으로만 흐르지 않고, 오른쪽에서 왼쪽으로도(역방향으로도) 영향을 미칩니다. 이것이 바로 양방향성(Bipolarity/Bidirectional)입니다.</p>
<p>수식 (a == 1) -&gt; (b == 1)은 명제 논리학의 대우(Contrapositive)에 의해 (b == 0) -&gt; (a == 0)과 완벽하게 동일합니다.</p>
<p>즉, 제약 조건 해결기(Solver)는 a를 먼저 정하고 b를 정하는 것이 아닙니다. b를 먼저 0으로 뽑아버리고, 그 결과에 맞춰 강제로 a를 0으로 만들어버릴 수도 있습니다.</p>
<p>현업의 해결책:
이 역방향 간섭을 끊고 내가 원하는 대로 확률을 조절하기 위해 사용하는 키워드가 바로 solve ... before ... 입니다.</p>
<pre><code class="language-c">constraint c_order {
  solve a before b; 
  (a == 1) -&gt; (b == 1);
}</code></pre>
<p>동작 원리: &quot;확률 공간을 계산할 때, b의 상태를 보지 말고 일단 무조건 a부터 50:50의 독립적인 확률로 뽑아라(Solve). 그 다음에 그 결과에 맞춰 b를 뽑아라.&quot;</p>
<p>주의할 점: 이 키워드는 변수에 값이 대입되는 &#39;시간적 순서&#39;를 바꾸는 것이 아닙니다. 오직 &#39;확률의 분포(Probability Distribution)&#39;를 강제하기 위해 사용됩니다.</p>
<h2 id="a-bidirectional-nature-양방향성의-정의">A. Bidirectional Nature (양방향성의 정의)</h2>
<p>&quot;Unlike procedural code, SystemVerilog constraints are bidirectional. Variables on the right side of an expression can influence the variables on the left side.&quot;</p>
<p>(절차적 코드와 달리 SystemVerilog 제약 조건은 양방향입니다. 수식의 오른쪽에 있는 변수가 왼쪽에 있는 변수에 영향을 미칠 수 있습니다.)</p>
<h2 id="b-the-problem-확률-왜곡">B. The Problem (확률 왜곡)</h2>
<p>&quot;Because the solver evaluates all valid combinations simultaneously, the probability distribution of one variable can be skewed by the constraints of another.&quot;</p>
<p>(Solver가 모든 유효한 조합을 동시에 평가하기 때문에, 한 변수의 확률 분포가 다른 변수의 제약 조건에 의해 왜곡될 수 있습니다.)</p>
<h2 id="c-the-solution-해결책">C. The Solution (해결책)</h2>
<p>&quot;To fix this, we use the solve before construct. It does not change the valid state space, but it forces the solver to determine the probability of the first variable independently, fixing the skewed distribution.&quot;</p>
<p>(이를 해결하기 위해 solve before 구문을 사용합니다. 이는 유효한 상태 공간을 바꾸지는 않지만, Solver가 첫 번째 변수의 확률을 독립적으로 결정하도록 강제하여 왜곡된 분포를 바로잡습니다.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cache]]></title>
            <link>https://velog.io/@houston_guy2/Cache</link>
            <guid>https://velog.io/@houston_guy2/Cache</guid>
            <pubDate>Sun, 17 May 2026 23:33:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/houston_guy2/post/50e2694f-038e-4957-b54c-e557cbea7ef9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b6f45209-e9ed-4e8a-be00-9c7a6682c6f4/image.png" alt=""></p>
<p>L1 Cache: 프로세서와 가장 가까운 캐시. 속도를 위해 I$ 와 D$로 나뉜다.</p>
<ul>
<li>Instruction Cache (I$): 메모리의 TEXT 영역 데이터를 다루는 캐시.</li>
<li>Data Cache (D$): TEXT 영역을 제외한 모든 데이터를 다루는 캐시.</li>
</ul>
<p>L2 Cache: 용량이 큰 캐시. 크기를 위해 L1 캐시처럼 나누지 않는다.
L3 Cache: 멀티 코어 시스템에서 여러 코어가 공유하는 캐시.</p>
<h2 id="cache-metrics">Cache Metrics</h2>
<p>캐시의 성능을 측정할 때는 히트 레이턴시(Hit latency)와 미스 레이턴시(Miss latency)가 중요한 요인으로 꼽힌다.</p>
<ul>
<li>Cache Hit: CPU에서 요청한 데이터가 캐시에 존재하는 경우</li>
<li>Hit latency: 히트가 발생해 캐싱된 데이터를 가져올 때 소요되는 시간</li>
<li>Cache Miss: 요청한 데이터가 캐시에 존재하지 않는 경우</li>
<li>Miss latency: 미스가 발생해 상위 캐시에서 데이터를 가져오거나(L1 캐시에 데이터가 없어서 L2 캐시에서 데이터를 찾는 경우) 메모리에서 데이터를 가져올 때 소요되는 시간을 말한다.</li>
</ul>
<p>캐시의 성능을 높이기 위해서는 캐시의 크기를 줄여 히트 레이턴시를 줄이거나, 캐시의 크기를 늘려 미스 비율을 줄이거나, 더 빠른 캐시를 이용해 레이턴시를 줄이는 방법이 있다.</p>
<h2 id="cache-organization">Cache Organization</h2>
<p>캐시는 반응 속도가 빠른 SRAM(Static Random Access Memory)으로, 주소가 키(Key)로 주어지면 해당 공간에 즉시 접근할 수 있다. 이러한 특성은 DRAM(Dynamic Random Access Meomry)에서도 동일하지만 하드웨어 설계상 DRAM은 SRAM보다 느리다. 통상적으로 &#39;메인 메모리’라고 말할 때는 DRAM을 의미한다.</p>
<p>주소가 키로 주어졌을 때 그 공간에 즉시 접근할 수 있다는 것은 캐시가 하드웨어로 구현한 해시 테이블(Hash table)과 같다는 의미다. 캐시가 빠른 이유는 자주 사용하는 데이터만을 담아두기 때문이기도 하지만, 해시 테이블의 시간 복잡도가 $O(1)$정도로 빠르기 때문이기도 하다.</p>
<p>캐시는 블록(Block)으로 구성되어 있다. 각각의 블록은 데이터를 담고 있으며, 주소값을 키로써 접근할 수 있다. 블록의 개수(Blocks)와 블록의 크기(Block size)가 캐시의 크기를 결정한다.</p>
<p><a href="https://parksb.github.io/article/29.html">https://parksb.github.io/article/29.html</a></p>
<h2 id="캐시-계층hierarchy과-동작의-핵심">캐시 계층(Hierarchy)과 동작의 핵심</h2>
<p>기본 개념: CPU는 속도가 매우 빠르기 때문에, 느린 DRAM(메인 메모리)에 직접 접근하지 않고 중간에 있는 L1 캐시를 통해 데이터를 주고받는다.</p>
<p>Hit (Match): CPU가 요청한 주소의 데이터가 캐시에 있을 때.</p>
<ul>
<li>동작: DRAM을 거치지 않고 캐시에서 즉시 CPU로 전달 (최고 속도).</li>
</ul>
<p>Miss (Mismatch): CPU가 요청한 주소의 데이터가 캐시에 없을 때.</p>
<ul>
<li>동작: 캐시가 DRAM으로부터 필요한 데이터를 새로 가져와야 함 (Allocate 과정).</li>
</ul>
<h2 id="캐시-miss-발생-시-처리-과정">캐시 Miss 발생 시 처리 과정</h2>
<p>Step 1 (Miss 판별): CPU가 요청한 주소가 캐시에 없음을 확인.</p>
<p>Step 2 (Eviction - 방 빼기): DRAM에서 새 데이터를 가져와야 하는데 캐시가 꽉 찼다면, 기존 데이터를 DRAM으로 쫓아냄.</p>
<ul>
<li>주의: 쫓겨나는 데이터가 &#39;Dirty(수정됨)&#39; 상태라면, 반드시 DRAM에 먼저 기록(Write-Back)해야 데이터가 유실되지 않음.</li>
</ul>
<p>Step 3 (Fill - 새로 채우기): DRAM에서 새로 필요한 데이터를 읽어와 캐시에 저장.</p>
<p>Step 4 (Respond - 응답): CPU에게 데이터를 전달.</p>
<h2 id="검증-엔지니어의-핵심-검증-포인트">검증 엔지니어의 핵심 검증 포인트</h2>
<p>Hit/Miss 판별 로직: 주소와 태그(Tag) 비교가 정확한가? (X-State 등 무효값 처리)</p>
<p>데이터 무결성: DRAM에서 데이터를 가져오거나(Fill) 쫓겨날 때(Evict), 데이터의 값이 변형되지 않는가?</p>
<p>충돌 방지 (Bypass): 캐시에 데이터를 쓰고 있는(Write) 도중에 CPU가 읽으려 할 때(Read), 엉뚱한 데이터를 읽지 않도록 설계되었는가?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DUT: Cache controller]]></title>
            <link>https://velog.io/@houston_guy2/DUT-Cache-controller</link>
            <guid>https://velog.io/@houston_guy2/DUT-Cache-controller</guid>
            <pubDate>Sun, 17 May 2026 23:11:27 GMT</pubDate>
            <description><![CDATA[<p>2-Stage 파이프라인, Direct-Mapped, Write-Back, Write-Allocate&quot; 방식을 사용하는 캐시 컨트롤러</p>
<h1 id="background-knowledge">Background Knowledge</h1>
<h2 id="l1-cache">L1 Cache</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f1a906f9-0b85-4ce6-9ad0-b08a4a191d49/image.png" alt="">
L1 Cache (Level 1 Cache, 1차 캐시)는 간단히 말해 CPU와 메인 메모리(RAM) 사이에 존재하는 &#39;가장 빠르고, 가장 작으며, CPU와 가장 가까운 임시 저장소</p>
<p>l1_cache_core.sv가 CPU의 비서 역할</p>
<ol>
<li>학생이 &quot;A번 책 줘!&quot;라고 요청했을 때, 책상 위에 그 책이 있으면 바로 줍니다. (Cache Hit)</li>
<li>책상에 없으면 학생에게 &quot;잠시만요!(Stall)&quot;라고 외치고, 도서관(RAM)으로 달려가서 책을 가져와 책상에 올려두고 학생에게 건네줍니다. (Cache Miss &amp; Allocate)</li>
<li>책상이 꽉 찼는데 새 책을 가져와야 하면, 책상 위에서 더러워진(낙서한) 책을 찾아 도서관에 정식으로 반납하고 옵니다. (Write-Back)</li>
</ol>
<h2 id="2-stage-pipeline">2-stage pipeline</h2>
<p>개념: 하나의 캐시 요청을 처리하는 과정을 두 단계로 쪼개어 공장 컨베이어 벨트처럼 돌리는 것입니다.</p>
<ul>
<li>Stage 1: 주소 쪼개기 + SRAM 읽기 시작</li>
<li>Stage 2: 진짜 내 데이터가 맞는지(Hit/Miss) 검사 + CPU에 응답</li>
</ul>
<p>결론부터 말씀드리면, &#39;파이프라인(Pipeline)&#39;이라는 공장 시스템(컨베이어 벨트)을 도입했다는 개념만 같을 뿐, 일하는 공장 자체가 다릅니다.</p>
<p>학교에서 배운 5-Stage Pipeline (CPU Core):
이건 &quot;명령어(Instruction)를 처리하는 중앙 처리 장치(CPU) 본체&quot;의 컨베이어 벨트입니다.
&quot;메모리에서 명령어를 가져와서(F), 무슨 뜻인지 해독하고(D), 더하기/빼기 연산을 하고(E), 메모리에 접근하고(M), 결과를 저장하는(W)&quot; 5단계를 거칩니다.</p>
<p>지금 우리가 짜는 2-Stage Pipeline (L1 Cache Controller):
이건 CPU 코어 외부에 붙어있는 &quot;데이터 창고(비서)의 컨베이어 벨트&quot;입니다.
위의 CPU 5단계 중 &#39;Memory(M) 단계&#39;에서 CPU가 &quot;야 캐시야, 나 A 주소 데이터 좀 줘!&quot; 라고 요청을 던지면, 그때부터 이 캐시 컨트롤러 내부의 2단계 컨베이어 벨트가 독자적으로 돌아가는 것입니다.</p>
<h2 id="direct-mapped">Direct-Mapped</h2>
<p>개념: 주소의 특정 부분(Index)을 보고, 캐시 메모리의 &#39;정해진 딱 한 자리&#39;에만 데이터를 저장하는 무식하지만 빠른 방식입니다. 도서관(RAM)의 책을 가져와 책상(Cache)에 놓을 때, 1번부터 256번까지 지정석을 만들어 두고 무조건 그 자리에만 놓는 것과 같습니다.</p>
<p>왜 검증이 어려울까? 서로 다른 데이터인데 우연히 &#39;지정석 번호(Index)&#39;가 똑같을 수 있습니다. 이를 충돌 미스(Conflict Miss)라고 합니다. A 데이터와 B 데이터가 같은 자리를 놓고 계속 서로를 쫓아내는 핑퐁 게임(Thrashing)이 일어날 때 로직이 꼬이지 않는지 확인해야 합니다.</p>
<h2 id="write-back-지연-쓰기">Write-Back (지연 쓰기)</h2>
<p>개념: CPU가 데이터를 썼을 때, 곧바로 저 멀리 있는 메인 메모리(RAM)까지 달려가서 업데이트하지 않고, 일단 가까운 캐시에만 쓴 뒤 &quot;이 데이터는 수정됐음!(Dirty=1)&quot; 이라고 표시만 해두는 방식입니다.</p>
<p>왜 검증이 어려울까? 나중에 새로운 데이터를 캐시에 올리기 위해 방 빼야 할 때(Eviction), 이 녀석이 수정된 적이 있는지(Dirty == 1) 확인하고 메인 메모리로 쫓아내는(Write-Back) 엑스트라 로직이 들어갑니다. 제가 코드에 심어놓은 버그 중 하나가 바로 이 Write-Back 과정에서 메인 메모리 주소를 잘못 계산하는 버그입니다!</p>
<h3 id="cpu-의-write-back-과-cache의-write-back-차이">CPU 의 write-back 과 Cache의 write-back 차이</h3>
<p>이름만 똑같을 뿐, 타겟(목적지)이 완전히 다른 개념입니다.</p>
<p>CPU 파이프라인의 5번째 단계: &quot;Write-back (WB)&quot;</p>
<ul>
<li>무엇을? ALU 연산 결과(예: 1+1=2)나 메모리에서 읽어온 데이터를</li>
<li>어디에? CPU 내부에 있는 아주 작은 &#39;레지스터 파일(Register File, 예: R1, R2)&#39;에</li>
<li>언제? 명령어가 끝나는 즉시 써넣는 단계입니다.</li>
</ul>
<p>Cache 컨트롤러의 정책: &quot;Write-back (지연 쓰기 정책)&quot;</p>
<ul>
<li>무엇을? CPU가 새로 저장하라고 준 데이터를</li>
<li>어디에? 저 멀리 있는 &#39;메인 메모리(DRAM)&#39;에</li>
<li>언제? 당장 쓰지 않고 캐시에만 임시로 써뒀다가(Dirty=1), 나중에 캐시 자리가 꽉 차서 데이터를 쫓아내야 할 때(Eviction) 비로소 메모리에 업데이트하는 &#39;정책&#39;입니다.</li>
</ul>
<p>참고: 반대말은 Write-through(CPU가 쓸 때마다 매번 메인 메모리까지 달려가서 동시에 쓰는 무식한 방식)입니다.</p>
<h2 id="write-allocate-쓰기-할당">Write-Allocate (쓰기 할당)</h2>
<p>개념: CPU가 특정 주소에 데이터를 쓰려고 했는데, 캐시에 그 주소가 없는 경우(Write Miss)입니다. 이때 &quot;어차피 쓰는 거니까 캐시 무시하고 메모리에 바로 쓰자(No-Write-Allocate)&quot;가 아니라, &quot;일단 메모리에서 캐시로 한 줄(16 Byte)을 통째로 퍼온 다음(Allocate), 그 위에 CPU가 준 데이터를 덮어쓰자!&quot;는 방식입니다.</p>
<p>왜 검증이 어려울까? 쓰기 작업인데도 불구하고 마치 읽기 작업처럼 메인 메모리에서 데이터를 퍼오는 과정(Read from RAM)이 선행되어야 하므로 FSM 상태 머신이 굉장히 복잡하게 빙글빙글 돕니다.</p>
<hr>
<h1 id="stage">Stage</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a62010da-e88d-4759-b901-cc80c2d9d5d6/image.png" alt=""></p>
<h2 id="1-stage-1-요청-수신-및-해독-request--decode">1. Stage 1: 요청 수신 및 해독 (Request &amp; Decode)</h2>
<p>CPU가 데이터를 달라고 주소(cpu_req_addr)를 던지면 가장 먼저 일어나는 일입니다.</p>
<ul>
<li><p>주소 쪼개기 (Decoding): 32비트 주소를 잘라서 캐시 라인을 찾을 Index(8비트)와, 진짜 내 데이터가 맞는지 확인할 Tag(20비트)로 나눕니다.</p>
</li>
<li><p>SRAM 읽기 준비: 잘라낸 Index를 이용해 파운드리에서 가져온 Tag SRAM과 Data SRAM에 읽기 주소(raddr)를 꽂아 넣습니다.</p>
</li>
<li><p>파이프라인 레지스터 저장: CPU가 보낸 요청 정보(Valid, Read/Write, 주소, 데이터)를 플립플롭에 저장하여 다음 클럭(Stage 2)으로 넘길 준비를 합니다.</p>
</li>
</ul>
<h2 id="2-stage-2-태그-확인-및-적중-판별-tag-check--data-access">2. Stage 2: 태그 확인 및 적중 판별 (Tag Check &amp; Data Access)</h2>
<p>Stage 1에서 보낸 정보가 다음 클럭에 여기로 넘어옵니다. (동시에 SRAM에서도 데이터가 튀어나옵니다.)</p>
<p>Cache Hit (캐시 적중): SRAM에서 튀어나온 Tag와 CPU가 요청한 Tag가 똑같고, 데이터가 유효하다(Valid == 1)면 Hit입니다!</p>
<p>Read Hit: 읽어온 데이터를 CPU에게 바로 던져줍니다 (cpu_rsp_valid = 1).</p>
<p>Write Hit: CPU가 쓰라고 준 데이터를 SRAM에 쓰고, &quot;이 데이터는 수정됐어!&quot;라는 표시로 Dirty Bit를 1로 만듭니다.</p>
<h2 id="3-캐시-미스-해결사-fsm-상태-머신">3. 캐시 미스 해결사: FSM (상태 머신)</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/7bc7b662-2236-4d17-aca3-d135b3d041e8/image.png" alt=""></p>
<p>만약 Stage 2에서 Tag가 다르거나 Valid가 0이면 Cache Miss가 발생합니다. 이때 캐시는 하던 일을 멈추고(pipeline_stall = 1), 아래 3단계 상태 머신(FSM)을 가동합니다.</p>
<p>[ST_IDLE] 평시 상태: 평소엔 여기서 대기하다가 미스가 나면 다음 상태로 넘어갑니다.</p>
<p>[ST_WRITE_BACK] 대피 상태 (조건부): 만약 기존 자리에 있던 데이터가 수정된 적이 있다면(Dirty == 1), 덮어씌워지기 전에 메인 메모리로 안전하게 쫓아냅니다. (이곳에 제가 치명적인 주소 버그를 숨겨두었습니다!)</p>
<p>[ST_ALLOCATE] 할당 상태: 메인 메모리에 &quot;진짜 데이터 내놔!&quot;라고 요청(mem_req_rw = 0)합니다. 메모리에서 데이터가 도착하면 SRAM에 채워 넣고 다시 ST_IDLE로 돌아갑니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AXI4-Lite Verification]]></title>
            <link>https://velog.io/@houston_guy2/AXI4-Lite-Verification</link>
            <guid>https://velog.io/@houston_guy2/AXI4-Lite-Verification</guid>
            <pubDate>Tue, 12 May 2026 03:17:40 GMT</pubDate>
            <description><![CDATA[<h1 id="block-diagram">Block Diagram</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/00fa6d40-094e-4fbc-9b60-1d83da790c3f/image.png" alt=""></p>
<h1 id="file-descprition">File Descprition</h1>
<h3 id="1-rtl-폴더-검증-대상-하드웨어">1. rtl/ 폴더 (검증 대상 하드웨어)</h3>
<p><span style = "color:lightgreen"> axi4_lite_slave.sv</span></p>
<ul>
<li>우리가 검증해야 할 타겟 하드웨어, 즉 DUT(Design Under Test)입니다. 다이어그램 상의 AXI4-LITE SLAVE 블록에 해당합니다.</li>
</ul>
<h3 id="2-tb-폴더-테스트벤치-최상단-및-환경-설정">2. tb/ 폴더 (테스트벤치 최상단 및 환경 설정)</h3>
<p><span style = "color:lightgreen">axi4_lite_if.sv</span></p>
<ul>
<li>UVM 테스트벤치와 DUT 사이를 연결해주는 핀(Pin)들의 묶음인 인터페이스(Interface)입니다. 다이어그램 중앙의 AWADDR, WDATA 등의 신호들이 정의되어 있습니다.</li>
</ul>
<p><span style = "color:lightgreen">tb_top.sv</span></p>
<ul>
<li>하드웨어(DUT)와 인터페이스를 인스턴스화하고 클럭/리셋을 생성하며, run_test()를 호출하여 UVM 환경을 구동시키는 최상단 모듈입니다.</li>
</ul>
<p><span style = "color:lightgreen">run.do</span></p>
<ul>
<li>시뮬레이터(ModelSim/Questa 등)에서 컴파일 및 시뮬레이션 실행을 자동화하는 매크로/스크립트 파일입니다.</li>
</ul>
<h3 id="3-src-폴더-uvm-핵심-컴포넌트">3. src/ 폴더 (UVM 핵심 컴포넌트)</h3>
<h4 id="span-style--colorskyblue-기본-데이터-및-시퀀스span"><span style = "color:skyblue"> [기본 데이터 및 시퀀스]</span></h4>
<p><span style = "color:lightgreen">sy_transaction.sv</span>
AXI4-Lite 버스를 타고 다니는 데이터 패킷(Address, Data, Control 등)을 정의한 클래스입니다. rand 변수들이 이곳에 선언되어 무작위 검증(CRV)의 기반이 됩니다.</p>
<p><span style = "color:lightgreen">sy_sequence.sv</span>
sy_transaction 객체들을 생성하여 시퀀서(Sequencer)로 보내는 기본적인 트래픽 생성기입니다.</p>
<pre><code>UVM 내장 함수
- start_item(req); (버스 사용 허가 요청)
시퀀서(Sequencer)에게 &quot;나 드라이버한테 보낼 데이터가 생겼는데, 지금 버스(Driver) 써도 돼?
- finish_item(req); (데이터 전송 및 대기)
허락이 떨어지면 드라이버에게 실제로 데이터를 쏴줍니다.
그리고 드라이버가 핀 레벨 제어를 완전히 끝내고 &quot;전송 완료(item_done)&quot; 신호를 보내줄 때까지 또 기다려줍니다.</code></pre><pre><code class="language-c">if (!req.randomize() with { trans_kind == WRITE; })</code></pre>
<p>req.randomize() with { trans_kind == WRITE; }
트랜잭션 내부의 변수들(addr, data 등)에 무작위 값을 채워 넣으라는 명령입니다. 단, with {} 블록을 써서 &quot;종류(trans_kind)는 무조건 WRITE로 고정해라&quot;라는 강제 조건(Inline Constraint)을 달았습니다.</p>
<p>함수의 반환값 (성공=1, 실패=0)
if (! ... ) (안전장치): 앞에 느낌표(!)가 붙었으므로 조건이 뒤집힙니다. 즉, &quot;만약 값 무작위 생성에 실패(0)했다면&quot; 이 if 문 안으로 들어가게 됩니다.</p>
<h4 id="span-style--colorskybluevirtual-sequencer--sequencesspan"><span style = "color:skyblue">[Virtual Sequencer &amp; Sequences]</span></h4>
<p>여러 시나리오를 지휘하는 역할을 합니다.</p>
<p><span style = "color:lightgreen">sy_virtual_sequencer.sv</span></p>
<ul>
<li>하위 시퀀서(sy_sequencer)들을 제어하기 위한 가상 시퀀서입니다.<pre><code class="language-c">uvm_sequencer #(sy_transaction) axi_sqr;</code></pre>
오직 AXI 트랜잭션(sy_transaction) 규격의 패킷만 취급하는 전용 우체국(Sequencer)의 연락처를 axi_sqr라는 이름으로 만들겠다</li>
</ul>
<p><span style = "color:lightgreen">sy_vseq_base.sv</span></p>
<ul>
<li>가상 시퀀스들의 공통 속성을 정의한 부모(Base) 클래스입니다.</li>
</ul>
<p><span style = "color:lightgreen">sy_vseq_sweep.sv</span></p>
<ul>
<li>전체 주소 대역을 순차적 혹은 무작위로 훑는(Sweep) 정상적인 통신 시나리오입니다.</li>
</ul>
<p><span style = "color:lightgreen">sy_vseq_error.sv</span></p>
<ul>
<li>다이어그램에 명시된 Error Injection Sequence입니다. 의도적으로 허용되지 않은 주소나 잘못된 제어 신호를 보내 DUT가 에러를 잘 뱉어내는지 테스트합니다.<pre><code class="language-c">start_item(tx, -1, axi_sqr)</code></pre>
start_item(보낼 물건, 우선순위, 목적지 우체국);</li>
</ul>
<p><span style = "color:lightgreen">sy_vseq_rmw.sv</span></p>
<ul>
<li>Read-Modify-Write 시퀀스입니다. 동일한 주소에 읽고, 수정하고, 다시 쓰는 동작을 스트레스 테스트합니다.<pre><code class="language-c">// .read(status, rdata, path, extension, parent)
</code></pre>
</li>
</ul>
<p>regmodel.ctrl_reg.read(status, rdata, UVM_FRONTDOOR, null, this);</p>
<pre><code>regmodel.ctrl_reg: regmodel 안에 있는 컨트롤 레지스터ctrl_reg 를 지칭합니다.

#### &lt;span style = &quot;color:skyblue&quot;&gt;[Agent 컴포넌트 (버스와의 직접적인 상호작용)]&lt;/span&gt;

&lt;span style = &quot;color:lightgreen&quot;&gt;sy_agent.sv&lt;/span&gt;
- Sequencer, Driver, Monitor를 하나로 묶어주는 컨테이너 역할을 합니다.

&lt;span style = &quot;color:lightgreen&quot;&gt;sy_driver.sv&lt;/span&gt;
- 시퀀스로부터 받은 트랜잭션을 실제 AXI4-Lite 프로토콜 타이밍(VALID/READY 핸드셰이크)에 맞춰 인터페이스(핀)에 구동합니다.

&lt;span style = &quot;color:lightgreen&quot;&gt;sy_monitor.sv&lt;/span&gt;
- 인터페이스(핀)의 신호를 수동적으로 지켜보다가, 유효한 통신이 발생하면 이를 묶어 트랜잭션 객체로 변환한 뒤 Analysis Port를 통해 Scoreboard와 Subscriber로 쏴줍니다.
```c
// Analysis Port
item_collected_port = new(&quot;item_collected_port&quot;, this);

// run phase
/ 1. 핀 데이터를 트랜잭션 객체에 옮겨 담기
tx.addr = vif.araddr; 
// 2. 안테나를 통해 스코어보드로 던짐
item_collected_port.write(tx);</code></pre><p>item_collected_port는 버스에서 수집한 신호들을 sy_transaction 객체로 포장한 뒤, Scoreboard 나 Coverage Collector 등 다른 컴포넌트들에게 Broadcast하기 위해 설치하는 전용 송신 안테나</p>
<h4 id="span-style--colorskyblue검증-및-커버리지-결과-확인span"><span style = "color:skyblue">[검증 및 커버리지 (결과 확인)]</span></h4>
<p>다이어그램 우측의 SCOREBOARD와 COVERAGE COLLECTOR 블록입니다.</p>
<p><span style = "color:lightgreen">sy_scoreboard.sv</span></p>
<ul>
<li>모니터가 보내온 데이터를 바탕으로 Reference Model(가짜 메모리, 연관 배열)을 업데이트하고, DUT가 뱉은 실제 값과 예상 값을 비교(Compare)하여 에러를 잡아냅니다.<pre><code class="language-c">uvm_analysis_imp #(sy_transaction, sy_scoreboard) item_collected_export;</code></pre>
이전 대화에서 모니터(Monitor)가 데이터를 방송하기 위해 uvm_analysis_port라는 송신 안테나를 세운 것을 확인하셨을 것입니다.</li>
<li>이 uvm_analysis_imp는 그 방송을 수신하는 전용 수신기입니다.</li>
<li>환경(Env) 단에서 모니터의 port와 스코어보드의 export를 선으로 연결(connect)해 주면, 모니터가 데이터를 쏠 때마다 스코어보드의 write() 함수가 자동으로 호출되며 트랜잭션이 넘어오게 됩니다.</li>
</ul>
<p>Associative Array (연관배열)</p>
<pre><code class="language-c">logic [31:0] ref_memory [logic [31:0]];</code></pre>
<pre><code>앞부분 logic [31:0] : 가로 길이 (데이터의 크기)
의미: 이 메모리의 한 칸(방)에 들어가는 데이터의 크기가 32비트(4바이트)라는 뜻입니다.
질문자님이 말씀하신 &quot;32비트고&quot;에 해당하는 부분이며, 이는 메모리의 가로폭을 의미합니다.

뒷부분 [logic [31:0]] : 세로 길이 (주소의 개수)
의미: 이 메모리의 방 번호(Index/Address)를 매길 때 32비트짜리 숫자를 쓴다는 뜻입니다.
오해의 원인: 세로로 32개가 있는 것이 아닙니다. 32비트로 표현할 수 있는 숫자의 경우의 수는 $2^{32}$입니다. 즉, 세로로 최대 4,294,967,296개(약 43억 개)의 방을 가질 수 있다는 뜻입니다
만약 코드를 일반적인 하드웨어 배열 문법으로 짰다면 이렇게 됩니다.
logic [31:0] ref_memory [0:4294967295];</code></pre><p><span style = "color:lightgreen">sy_subscriber.sv</span></p>
<ul>
<li>100% 검증이 완료되었는지 확인하기 위해 Functional Coverage를 수집(covergroup, coverpoint)하는 컴포넌트입니다.</li>
</ul>
<h4 id="span-style--colorskyblueral-register-abstraction-layer---레지스터-자동화-검증span"><span style = "color:skyblue">[RAL (Register Abstraction Layer) - 레지스터 자동화 검증]</span></h4>
<p>다이어그램에는 생략되어 있지만 매우 중요한 고급 UVM 기능입니다.</p>
<p><span style = "color:lightgreen">sy_reg_block.sv</span></p>
<ul>
<li>DUT 내부의 레지스터 맵(주소, 권한 등)을 소프트웨어적으로 모델링한 파일입니다.<pre><code class="language-c">status_reg.configure(this, null);</code></pre>
.configure(...): UVM이 제공하는 함수로, 방금 갓 태어난 레지스터에게 &quot;너의 부모(Parent)가 누구인지&quot; 지정해 주는 역할을 합니다.</li>
</ul>
<p><span style = "color:lightgreen">sy_reg_adapter.sv</span></p>
<ul>
<li>UVM 레지스터 트랜잭션(RAL)과 AXI4-Lite 트랜잭션(sy_transaction) 사이의 형태를 변환해주는 어댑터입니다.</li>
</ul>
<p><span style = "color:lightgreen">sy_ral_sequence.sv</span></p>
<ul>
<li>RAL 모델을 사용하여 DUT의 레지스터를 편하게 읽고 쓰는(Front-door/Back-door access) 시퀀스입니다.</li>
</ul>
<h4 id="span-style--colorskyblue최상위-컴포넌트-및-패키지span"><span style = "color:skyblue">[최상위 컴포넌트 및 패키지]</span></h4>
<p><span style = "color:lightgreen">sy_env.sv</span>
Agent, Scoreboard, Subscriber, Virtual Sequencer 등을 인스턴스화하고, 모니터와 스코어보드/서브스크라이버 간의 Analysis Port를 연결(Connect)하는 컨테이너입니다.</p>
<p><span style = "color:lightgreen">sy_test.sv</span>
UVM 최상단 컴포넌트(UVM TEST)입니다. sy_env를 생성하고, 어떤 테스트 시나리오(Virtual Sequence)를 돌릴지 결정하여 실행합니다.</p>
<p><span style = "color:lightgreen">sy_uvm_pkg.sv</span>
위의 모든 src/ 폴더 내의 SV 파일들을 올바른 순서로 include 하여 하나의 패키지로 묶어주는 파일입니다. 컴파일 에러를 방지합니다.</p>
<h3 id="where-is-the-seqeuncer">Where is the Seqeuncer?</h3>
<p>UVM에서 Sequencer는 특별한 추가 기능(예: 복잡한 중재(Arbitration) 로직이나 시퀀서 전용 변수)이 필요하지 않다면, 굳이 별도의 .sv 파일을 만들지 않는 것이 실무에서도 일반적인 관례입니다.</p>
<p>sy_agent.sv 파일 상단이나 sy_uvm_pkg.sv 내부에 다음과 같이 한 줄로 정의되어 있습니다</p>
<pre><code class="language-c">uvm_sequencer #(sy_transaction) sqr;</code></pre>
<p>왜 별도의 파일을 만들지 않았나요?
기본 기능만 사용: 단순히 시퀀스(Sequence)에서 받은 아이템을 드라이버(Driver)로 전달하는 &#39;큐(Queue)&#39; 역할만 수행한다면, UVM이 이미 제공하는 uvm_sequencer를 그대로 가져다 쓰기만 하면 됩니다.</p>
<hr>
<h2 id="intentional-error-injection--verification">Intentional Error Injection &amp; Verification</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ca90ded5-440b-479f-9111-b33a7c5cca0f/image.png" alt=""></p>
<h2 id="100-functional-coverage">100% Functional Coverage</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/feef78ea-822e-4704-a838-10aeaf07192a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hardware-based speculation]]></title>
            <link>https://velog.io/@houston_guy2/Hardware-based-speculation</link>
            <guid>https://velog.io/@houston_guy2/Hardware-based-speculation</guid>
            <pubDate>Tue, 28 Apr 2026 18:27:25 GMT</pubDate>
            <description><![CDATA[<h2 id="1-단순-예측의-한계-superscalar의-현실">1. 단순 예측의 한계 (Superscalar의 현실)</h2>
<p>한 사이클에 명령어를 4개, 8개씩 빨아들이는(Fetch) 슈퍼스칼라 구조에서는 단순히 분기 방향 하나 맞췄다고 파이프라인이 매끄럽게 돌아가지 않습니다. 명령어들 사이에 얽혀있는 데이터 의존성(Data Hazard) 때문에 ALU가 놀고 있는 시간이 생기기 때문입니다. 이 한계를 깨기 위해 두 가지 기술을 융합합니다.</p>
<p>동적 스케줄링 (Dynamic Scheduling): 컴파일러가 정해준 순서에 얽매이지 않고, 하드웨어가 런타임에 데이터가 준비된 명령어부터 연산기에 먼저 밀어 넣습니다.</p>
<p>추측 실행 (Speculative Execution): 앞선 분기문의 진짜 결과가 나오기도 전에, 예측기의 결과를 100% 맹신하고 그 너머의 명령어들을 미리 실행(Pre-execute)해 버립니다.</p>
<h2 id="2-파이프라인의-이원화-ooo-execute-vs-in-order-commit">2. 파이프라인의 이원화 (OOO Execute vs. In-order Commit)</h2>
<p>가장 중요한 슬라이드의 핵심입니다. 아키텍처 설계자들은 프로세서의 동작을 두 개의 독립적인 세계로 완전히 쪼개버렸습니다.</p>
<p>실행 (Out-of-order execution): 백엔드 연산기(ALU) 쪽은 말 그대로 무법지대입니다. 순서와 상관없이 오퍼랜드(Operand) 데이터만 준비되면 먼저 낚아채서 계산해 버립니다. 이를 통해 명령어 수준의 병렬성(ILP)을 극대화합니다.</p>
<p>확정 (In-order commit): 연산이 끝났다고 해서 그 결과값을 R1, R2 같은 메인 아키텍처 레지스터나 메모리에 즉시 덮어쓰지 않습니다. 무조건 프로그램에 적혀있던 원래 순서대로 차례가 돌아왔을 때만 영구적으로 반영(Commit) 합니다.</p>
<h2 id="3-핵심-철학-순서대로-가져오고-맘대로-실행하고-순서대로-확정한다">3. 핵심 철학: &quot;순서대로 가져오고, 맘대로 실행하고, 순서대로 확정한다&quot;</h2>
<p>하드웨어 기반 추측의 동작 원리는 다음 3단계 파이프라인 흐름으로 요약됩니다.</p>
<p>In-order Issue (순서대로 발급): 메모리에서 명령어들을 프로그램에 적힌 원래 순서대로 긁어옵니다.</p>
<p>Out-of-order Execute (비순차 실행): 데이터 의존성이 해결된 명령어부터 닥치는 대로 먼저 연산기(ALU)에 밀어 넣고 계산합니다. (여기서 순서가 완전히 뒤죽박죽 섞입니다.)</p>
<p>In-order Commit (순서대로 확정): 계산이 끝났다고 바로 레지스터에 값을 쓰지 않습니다. 원래 프로그램 순서대로 차례가 돌아왔을 때만 최종적으로 값을 씁니다.</p>
<h2 id="4-마법의-핵심-부품-rob-reorder-buffer">4. 마법의 핵심 부품: ROB (Reorder Buffer)</h2>
<p>비순차 실행으로 난장판이 된 순서를 다시 원래대로 끼워 맞추고, 예측이 틀렸을 때 파이프라인을 복구할 수 있게 해주는 가장 중요한 하드웨어 컴포넌트가 바로 ROB(재정렬 버퍼)입니다.</p>
<p>ROB는 일종의 원형 큐(Circular Queue) 형태의 하드웨어 테이블입니다.</p>
<p>명령어가 파이프라인에 들어올 때(Issue), 하드웨어는 무조건 ROB의 꼬리(Tail) 쪽에 빈 방을 하나 할당합니다.</p>
<p>연산기가 계산을 마치면, 그 결과값을 메인 레지스터(예: R1, R2)가 아니라 자기 자신에게 할당된 ROB의 방 안에 임시로 적어둡니다.</p>
<p>오직 예측이 맞았다는 것이 확정되고, 내 명령어가 ROB의 머리(Head) 부분에 도달했을 때만(Commit), 비로소 ROB에서 결과값을 빼내어 메인 레지스터에 영구적으로 기록합니다.</p>
<h2 id="5-예측-실패-시의-하드웨어-복구-hardware-flush">5. 예측 실패 시의 하드웨어 복구 (Hardware Flush)</h2>
<p>ROB가 존재하는 진짜 이유입니다. 만약 분기 예측기(BHT/BTB)가 틀렸다는 것이 뒤늦게 판명되면 어떻게 될까요?</p>
<p>하드웨어는 ROB를 쭉 스캔합니다.</p>
<p>틀린 분기문 이후에 섣불리(Speculatively) 실행되어 ROB에 임시로 결과값을 적어둔 모든 잉여 명령어들의 방을 단 1사이클 만에 한꺼번에 초기화(Flush)해 버립니다.</p>
<p>값들이 메인 레지스터에는 아직 쓰이지 않았기 때문에, 프로세서의 공식적인 상태(Architectural State)는 전혀 오염되지 않은 깔끔한 상태를 유지할 수 있습니다.</p>
<hr>
<p>고전적인 토마술로(Tomasulo) 알고리즘에 ROB(Reorder Buffer)를 추가하여 하드웨어 기반 추측 실행(HWBS)을 완성한 궁극의 마이크로아키텍처 동작 원리입니다.</p>
<h2 id="1-파이프라인의-4단계-생명주기-steps-of-execution">1. 파이프라인의 4단계 생명주기 (Steps of Execution)</h2>
<p>기존 파이프라인과 가장 큰 차이점은 결과 쓰기(Write result)와 최종 확정(Commit)이 물리적으로 완전히 분리되었다는 점입니다.</p>
<h4 id="1단계-issue-발급---in-order">1단계: Issue (발급) - In-order</h4>
<ul>
<li>메모리에서 명령어를 순서대로 가져옵니다.</li>
<li>연산기 앞의 대기열인 RS(Reservation Station)와 임시 장부인 ROB 양쪽 모두에 빈자리가 있어야만 명령어를 발급합니다. 하나라도 꽉 찼으면 파이프라인은 멈춥니다(Stall).</li>
</ul>
<h4 id="2단계-execute-실행---out-of-order">2단계: Execute (실행) - Out-of-order</h4>
<ul>
<li>연산에 필요한 데이터가 준비될 때까지 기다립니다.</li>
<li>다른 연산기가 공용 데이터 버스(CDB)에 내가 필요한 값을 던져주면(Broadcast), 그것을 낚아채서 즉시 연산기(ALU)에 밀어 넣습니다. 순서는 완전히 무시됩니다.</li>
</ul>
<h4 id="3단계-write-result-결과-임시-쓰기">3단계: Write result (결과 임시 쓰기)</h4>
<ul>
<li>연산이 끝난 값을 CDB에 태워 보냅니다.</li>
<li>핵심: 이 값은 메인 레지스터(R1, R2)로 절대 가지 않습니다. 오직 나를 기다리고 있던 다른 RS들과, 내게 할당된 ROB의 특정 칸에만 임시로 기록됩니다. 연산을 마친 RS 자리는 이제 비워져서 다음 명령어를 받을 준비를 합니다.</li>
</ul>
<h4 id="4단계-commit-최종-확정---in-order">4단계: Commit (최종 확정) - In-order</h4>
<ul>
<li>내 명령어가 ROB의 맨 앞줄(Head)에 도달할 때까지 얌전히 기다립니다.</li>
<li>내 차례가 왔고, 앞선 분기 예측들이 모두 맞았음이 확인되면, 비로소 ROB에 들고 있던 임시 값을 메인 레지스터나 메모리에 영구적으로 덮어씁니다.</li>
<li>Flush (복구): 만약 맨 앞줄에 도달한 명령어가 &#39;실패한 분기문&#39;이라면, 하드웨어는 뒤따라오던 잉여 명령어들이 담긴 ROB를 싹 다 비워버리고(Flush) 올바른 주소부터 다시 시작합니다.</li>
</ul>
<h2 id="2-rob의-물리적-구조-fields-in-rob">2. ROB의 물리적 구조 (Fields in ROB)</h2>
<p>ROB는 실제 RTL로 구현할 때 거대한 SRAM 레지스터 파일로 만들어집니다. 슬라이드에 적힌 필드들은 SRAM의 각 열(Column)을 의미합니다.</p>
<ul>
<li>Busy: 이 방이 현재 사용 중인가? (1-bit)</li>
<li>Instruction: 어떤 종류의 명령어인가? (분기문, 저장, 덧셈 등)</li>
<li>State: 현재 파이프라인 4단계 중 어디에 위치해 있는가?</li>
<li>Destination: 최종적으로 이 값을 덮어쓸 메인 레지스터 번호(예: R1)나 메모리 주소.</li>
<li>Value: 연산기에서 계산되어 날아온 32/64비트짜리 실제 데이터 값.</li>
</ul>
<h2 id="3-하드웨어-구조의-결정적-변화-rename-to-rob">3. 하드웨어 구조의 결정적 변화 (Rename to ROB)</h2>
<p>슬라이드 하단의 Register rename to ROB instead of RS는 프로세서 설계의 핵심입니다.</p>
<ul>
<li><p>기존 토마술로: 레지스터가 &quot;내 데이터는 3번 RS에서 계산 중이야&quot;라고 RS 번호(Tag)를 가리켰습니다.</p>
</li>
<li><p>HWBS (ROB 도입 후): RS는 연산이 끝나자마자 다음 명령어를 위해 자리를 비워줘야 합니다. 따라서 데이터를 계속 들고 있을 수 없습니다. 대신, 레지스터는 &quot;내 데이터는 ROB의 5번 방에 임시 보관되어 있어&quot;라고 ROB의 인덱스 번호를 가리키게(Rename) 됩니다. 실제 하드웨어 아키텍처를 설계하고 UVM으로 검증할 때, 가장 많은 코너 케이스 버그와 타이밍 위반이 터져 나오는 곳이 바로 이 ROB의 상태 전이와 비동기적인 Flush 신호가 맞물리는 부분입니다. 명령어들이 꼬이지 않고 정확한 타이밍에 Commit 되는지 확인하는 것은 설계 검증의 최대 난제입니다.</p>
</li>
</ul>
<h2 id="4-block-diagram">4. Block Diagram</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/8a0e58a8-9adb-48dd-847f-c59bb71cffb1/image.png" alt=""></p>
<h3 id="1-instruction-status-명령어-상태">1. Instruction Status (명령어 상태)</h3>
<p>첫 번째 명령어 L.D F6, 34(R2)가 메모리에서 성공적으로 Fetch되어 Issue 단계에 체크(✓) 표시가 되었습니다.</p>
<p>즉, 하드웨어가 이 명령어를 해독하고 내부 장치(RS와 ROB)에 자원을 성공적으로 할당했다는 뜻입니다.</p>
<h3 id="2-reservation-stations-rs---연산기-대기열">2. Reservation Stations (RS - 연산기 대기열)</h3>
<p>명령어가 발급되었으므로, 실제 연산을 수행할 대기열인 RS 테이블에 정보가 매핑됩니다.</p>
<p>할당: Load1 장치가 할당되어 Busy가 yes로 바뀝니다. 연산 종류(Op)는 Load입니다.</p>
<p>오퍼랜드 준비: * Vk 필드에 Regs[R2]가 기록되었습니다. 메모리 주소를 계산하기 위한 베이스 레지스터 R2의 값이 이미 준비되어 있다는 뜻입니다.</p>
<p>A 필드에 오프셋 값인 34가 기록되었습니다. ALU는 이제 이 두 값을 더해 실제 메모리 주소를 구하게 됩니다.</p>
<p>가장 중요한 구조적 변화 (Dest 필드): 이전 슬라이드에서 강조했던 Register rename to ROB instead of RS가 물리적으로 적용된 부분입니다. Dest 값이 RS 번호가 아니라 #1을 가리키고 있습니다. 이는 &quot;메모리에서 데이터를 가져오면, 메인 레지스터나 다른 곳이 아니라 무조건 ROB의 1번 방에 임시로 써넣어라&quot;라는 하드웨어의 엄격한 지시입니다.</p>
<h3 id="3-register-result-status-레지스터-상태-및-리네이밍">3. Register Result Status (레지스터 상태 및 리네이밍)</h3>
<p>이 아키텍처가 데이터 의존성(Data Hazard)을 뚫고 나가는 핵심 하드웨어 맵핑 구역입니다.</p>
<p>명령어의 목적지인 F6 레지스터 칸을 보십시오. 그곳에 #1 (Reorder #1) 이라는 꼬리표가 붙었고, Busy가 yes로 설정되었습니다.</p>
<p>의미: 하드웨어가 공용 데이터 버스(CDB)를 향해 이렇게 선언한 것과 같습니다. &quot;지금부터 F6 레지스터의 진짜 최신 값은 메인 레지스터에 없다. F6가 필요한 명령어들은 엉뚱한 곳 찾지 말고, ROB의 1번 방(Reorder #1)에서 데이터가 튀어나오기만을 기다려라.&quot;</p>
<hr>
<h1 id="현대판-비순차modern-ooo-파이프라인">현대판 비순차(Modern OOO) 파이프라인</h1>
<h2 id="1-아키텍처의-결정적-변화-데이터-이동의-최소화">1. 아키텍처의 결정적 변화 (데이터 이동의 최소화)</h2>
<p>고전적인 구조의 가장 큰 문제점은 데이터가 너무 많이 이사를 다닌다는 것이었습니다. 연산이 끝나면 RS에 썼다가, ROB에 썼다가, 마지막에 다시 메인 레지스터로 복사해야 했습니다. 이는 엄청난 전력 낭비와 칩 면적의 비효율을 초래합니다.</p>
<ul>
<li><p>물리 레지스터(Physical Registers)의 도입: 현대 칩 내부에는 어셈블리 코드에 보이는 R1, R2 (아키텍처 레지스터) 외에, P1부터 P256까지 수백 개의 거대한 물리 레지스터 파일(PRF)이 존재합니다.</p>
</li>
<li><p>ROB의 경량화: 데이터는 연산이 끝난 직후 무조건 PRF의 특정 방에 딱 한 번만 쓰이고 다시는 움직이지 않습니다. 이제 ROB는 무거운 실제 데이터를 들고 있지 않고, &quot;이 명령어의 결과값은 물리 레지스터 P45번에 들어있다&quot;라는 가벼운 포인터(영수증)만 들고 순서를 관리합니다.</p>
</li>
<li><p>용어의 현대화: 예전의 Reservation Station(RS)은 기능이 조금 더 통합되어 Issue Queue (IQ) 또는 Instruction Window라는 이름으로 불리게 되었습니다.</p>
</li>
</ul>
<h2 id="2-현대-ooo-파이프라인-스테이지-사이클-흐름">2. 현대 OOO 파이프라인 스테이지 (사이클 흐름)</h2>
<p>슬라이드 하단에 나열된 긴 스테이지들을 명령어가 파이프라인을 통과하는 시간순으로 쪼개어 보겠습니다.</p>
<ol>
<li><p>Fetch &amp; Decode (가져오기 &amp; 해독): 메모리에서 명령어를 순서대로(In-order) 가져와 정체를 파악합니다.</p>
</li>
<li><p>Register Renaming (이름표 바꿔 달기): 명령어에 적힌 R1 같은 가짜 이름(아키텍처 레지스터)을, 현재 비어있는 실제 물리 레지스터(예: P10)에 1:1로 매핑해 줍니다. 데이터 의존성(WAW, WAR Hazard)이 여기서 완벽하게 끊어집니다.</p>
</li>
<li><p>Dispatch (배치): 이름표를 바꿔 단 명령어를 실행 대기열인 Issue Queue와, 순서 확정을 위한 ROB 양쪽에 동시에 밀어 넣습니다.</p>
</li>
<li><p>Issue (발급 - Wakeup &amp; Select): 여기가 비순차(OOO) 마법이 시작되는 곳입니다.</p>
<ul>
<li><p>Wakeup: 계산에 필요한 오퍼랜드 데이터가 준비될 때까지 대기열에서 잠자코 기다립니다.</p>
</li>
<li><p>Select: 데이터가 준비된 명령어들이 손을 들면, 하드웨어가 그중에서 연산기(ALU)로 보낼 놈을 고릅니다.</p>
</li>
</ul>
</li>
<li><p>Read Register (레지스터 읽기): 연산기로 들어가기 직전, 배정받았던 물리 레지스터(PRF)에서 진짜 데이터를 읽어옵니다.</p>
</li>
<li><p>Execute (실행): ALU에서 실제 계산을 수행합니다.</p>
</li>
<li><p>Write back (결과 쓰기): 계산 결과를 물리 레지스터에 써넣습니다. 그리고 대기열(Issue Queue)에 있는 다른 명령어들에게 &quot;내 데이터 계산 끝났다!&quot;라고 방송(Broadcast)하여 그들을 깨웁니다.</p>
</li>
<li><p>Commit (최종 확정): 내 명령어가 ROB의 맨 앞줄에 도달했고 예측이 맞았다면, &quot;이제부터 공식적인 R1의 진짜 최신 값은 P10에 있다&quot;라고 시스템의 공식 장부(Architectural State)에 도장을 찍고 확정합니다.</p>
</li>
</ol>
<p>결과적으로 현대 아키텍처는 무거운 데이터를 이리저리 옮기는 대신, &#39;이름표(Tag)&#39;와 &#39;포인터&#39;만 파이프라인에서 빠르게 굴리며 성능과 전력 효율을 극대화한 것입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Robot Arm big picture]]></title>
            <link>https://velog.io/@houston_guy2/Robot-Arm-big-picture</link>
            <guid>https://velog.io/@houston_guy2/Robot-Arm-big-picture</guid>
            <pubDate>Mon, 27 Apr 2026 20:42:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/houston_guy2/post/98fcf42b-3556-4390-9d0b-b6f153afb26d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/741f3906-3027-4a40-8989-9d9085005da7/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cyber HW 5]]></title>
            <link>https://velog.io/@houston_guy2/Cyber-HW-5</link>
            <guid>https://velog.io/@houston_guy2/Cyber-HW-5</guid>
            <pubDate>Mon, 27 Apr 2026 19:19:58 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolororangex_3-equiv-s2---x_1---x_2-pmod-p-span"><span style="color:orange">$x_3 \equiv s^2 - x_1 - x_2 \pmod p$ </span></h3>
<h3 id="span-stylecolororangey_3-equiv-s-cdot-x_1---x_3---y_1-pmod-pspan"><span style="color:orange">$y_3 \equiv s \cdot (x_1 - x_3) - y_1 \pmod p$</span></h3>
<h3 id="span-stylecolororanges-equiv-fracy_2---y_1x_2---x_1-pmod-pspan"><span style="color:orange">$$s \equiv \frac{y_2 - y_1}{x_2 - x_1} \pmod p$$</span></h3>
<h3 id="span-stylecolororanges-equiv-frac3x_12--a2y_1-pmod-pspan"><span style="color:orange">$$s \equiv \frac{3x_1^2 + a}{2y_1} \pmod p$$</span></h3>
<h1 id="1-ecdh">1 ECDH</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/af421c93-217c-48e6-af5b-ca506e927a56/image.png" alt=""></p>
<p>개인 키 $a = 6$
상대방의 공개 키 $B = (5, 9)$
타원 곡선 $E: y^2 \equiv x^3 + x + 6 \pmod{11}$
(즉, $A = 1, B_{curve} = 6, p = 11$)</p>
<p> $B \rightarrow 2B \rightarrow 3B \rightarrow 6B$ 순서로 도출</p>
<ol>
<li>2B 구하기 (Point Doubling)
<img src="https://velog.velcdn.com/images/houston_guy2/post/de5ce1b2-936a-4e99-829b-99242ff0722c/image.png" alt=""></li>
</ol>
<ol start="2">
<li><p>3B 구하기 (Point Addition)
<img src="https://velog.velcdn.com/images/houston_guy2/post/5c73002c-4563-4c9c-80d0-5ab4dcb781e1/image.png" alt=""></p>
</li>
<li><p>6B 구하기 (Point Doubling)
<img src="https://velog.velcdn.com/images/houston_guy2/post/c463e621-9b6f-46ad-ad8f-a8d8464395d1/image.png" alt=""></p>
</li>
</ol>
<h1 id="2-rsa-verification">2 RSA verification</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/70d54354-0336-46a0-9a61-50b35066305b/image.png" alt=""></p>
<ol>
<li><p>$(x = 123, sig(x) = 6292)$
검증식: $6292^{131} \pmod{9797}$
연산 결과: $123$
판정: 연산 결과가 원본 메시지 $123$과 일치하므로 유효한 서명입니다.</p>
</li>
<li><p>$(x = 4333, sig(x) = 4768)$
검증식: $4768^{131} \pmod{9797}$
연산 결과: $4333$과 일치하지 않습니다.
판정: 유효하지 않은 서명입니다.</p>
</li>
<li><p>$(x = 4333, sig(x) = 1424)$
검증식: $1424^{131} \pmod{9797}$
연산 결과: $4333$
판정: 연산 결과가 원본 메시지 $4333$과 일치하므로 유효한 서명입니다.</p>
</li>
</ol>
<h1 id="3-mac-then-encrypt">3 MAC-then-Encrypt</h1>
<p>As we have seen, MACs can be used to authenticate messages. With this problem, we want to show the difference between two protocols—one with a MAC, one with a digital signature. In the two protocols, the sending party performs the following operation:</p>
<h3 id="1-protocol-a">1. protocol A</h3>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c6ffdc8a-8c9b-4c6a-91a2-ce565a3b9848/image.png" alt="">
where x is the message, h) is a hash function such as SHA-1, e is a private-key encryption algorithm, &quot;||&quot; denotes simple concatenation, and ki, kz are secret keys which are only known to the sender and the receiver.</p>
<p>문제의 핵심은 이 수식이 MAC(메시지 인증 코드) 기반의 프로토콜임을 인지하고, 이것이 디지털 서명(Digital Signature) 프로토콜과 어떤 결정적인 차이가 있는지 도출해내는 것입니다.</p>
<p>Key Difference: Non-repudiation</p>
<ul>
<li><p>MAC Protocol (Given): Uses shared secret keys ($k_1$, $k_2$). Since both parties know the keys, the receiver can also create the MAC. Thus, the sender can deny sending the message.</p>
</li>
<li><p>Digital Signature: Uses the sender&#39;s unique private key. The receiver cannot forge the signature, guaranteeing that the sender cannot deny sending the message (Provides non-repudiation).</p>
</li>
</ul>
<h3 id="2-protocol-b">2. Protocol B</h3>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/edd19cf1-d24a-4ce9-892e-180a2410d689/image.png" alt=""></p>
<p>Provide a step-by-step description (e.g., with an itemized list) of what the receiver does upon receipt of y. You may want to draw a block diagram for the process on the receiver&#39;s side, but that&#39;s optional.</p>
<p>Receiver&#39;s Step-by-Step Process:</p>
<ol>
<li>Decrypt: Decrypt the ciphertext $y$ using the shared symmetric key $k$ to retrieve $x || sig_{k_{pr}}(h(x))$.</li>
<li>Parse: Separate the original message $x$ and the digital signature $sig_{k_{pr}}(h(x))$.</li>
<li>Hash: Compute the hash of the received message independently to get a local $h(x)$.</li>
<li>Verify Signature: Decrypt the signature using the sender&#39;s public key $k_{pub}$ to extract the original $h(x)$.</li>
<li>Compare: Compare the local hash with the extracted hash. If they match, the message is authenticated and accepted.</li>
</ol>
<h1 id="4">4</h1>
<p>You have to choose the cryptographic algorithms for a KDC where two different classes of encryption occur:</p>
<ul>
<li>$e_{k_{U,KDC}}()$ , where U denotes an arbitrary network node (user),</li>
<li>$e_{k_{ses}}()$  for the communication between two users.</li>
</ul>
<p>You have the choice between two different algorithms, DES and 3DES (Triple-DES), and you are advised to use distinct algorithms for both encryption classes. Which algorithm do you use for which class? Justify your answer including aspects of security as well as celerity </p>
<p>Algorithm Assignment:
$e_{k_{U,KDC}}()$: 3DES
$e_{k_{ses}}()$: DES</p>
<ul>
<li><p>Security: The master key ($k_{U,KDC}$) is long-term and critical; if compromised, all session keys are exposed. Thus, it requires the higher security of 3DES. The session key ($k_{ses}$) is short-lived, so the lower security of DES is acceptable since an attack would only compromise a single, temporary session.</p>
</li>
<li><p>Celerity (Speed): User-to-user communication ($e_{k_{ses}}()$) involves encrypting large amounts of bulk data, which requires the faster processing speed of DES. Conversely, KDC-to-user communication only encrypts very small data (the session key itself), so the slower speed of 3DES does not cause performance issues.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Trouble shooting]]></title>
            <link>https://velog.io/@houston_guy2/Trouble-shooting</link>
            <guid>https://velog.io/@houston_guy2/Trouble-shooting</guid>
            <pubDate>Sun, 26 Apr 2026 05:15:43 GMT</pubDate>
            <description><![CDATA[<h1 id="시스템-통제의-4대-핵심-계층-macro-to-micro">시스템 통제의 4대 핵심 계층 (Macro to Micro)</h1>
<h2 id="1-전체-공정-지휘--run_chi_experiments">1. 전체 공정 지휘 : run_chi_experiments</h2>
<ul>
<li><p>역할: &quot;전극 집고 -&gt; 씻고 -&gt; EIS 측정하고 -&gt; 도금해라&quot;라는 전체 컨베이어 벨트의 타임라인을 쥐고 있는 메인 상태 머신입니다.</p>
</li>
<li><p>통제권: 전체 실험의 흐름을 통제합니다. 특정 검사 단계를 생략하거나 순서를 뒤바꿀 때 (예: &quot;시간 없으니 사전 EIS 검사는 빼고 바로 도금(CP)으로 넘어가&quot;) 조작해야 하는 최상위 스위치입니다.</p>
</li>
</ul>
<h2 id="2-개별-행동-제어-move_to_tube--chi">2. 개별 행동 제어: move_to_tube &amp; chi</h2>
<ul>
<li><p>역할: 최상위 모듈의 호출을 받아 실제 기계 장치를 구동하는 하위 구동부입니다.</p>
</li>
<li><p>통제권: 특정 순간의 단위 동작을 통제합니다. 로봇 팔의 세부적인 동선을 지시하거나 주사기를 조작할 때(move_to_tube), 또는 전위차계에 가할 전압/전류의 구체적인 크기와 시간 레시피를 바꿀 때(chi) 조작합니다.</p>
</li>
</ul>
<h2 id="3-물리적-공간-맵핑-좌표계-offset--pitch">3. 물리적 공간 맵핑: 좌표계 (Offset &amp; Pitch)</h2>
<ul>
<li><p>역할: 로봇 모터가 움직여야 할 목적지를 정량적인 수치로 변환해 주는 물리적 지도입니다.</p>
</li>
<li><p>통제권: 하드웨어의 배치(Layout)를 통제합니다. 비커 랙을 실수로 건드려 위치가 틀어졌거나, 새로운 규격의 비커를 가져왔을 때 로봇 팔의 영점(Calibration)을 다시 잡기 위해 조작합니다.</p>
</li>
</ul>
<h2 id="4-비상-생존-및-복구-reinit--kill">4. 비상 생존 및 복구: reinit &amp; kill</h2>
<ul>
<li><p>역할: 하드웨어나 통신망이 물리적인 한계로 뻗었을 때 시스템을 강제로 살려내는 심폐소생술입니다.</p>
</li>
<li><p>통제권: 예외 상황(Exception)을 통제합니다. 장비가 무한 대기(Deadlock)에 빠지거나 윈도우 COM 포트가 꼬였을 때, 사람의 개입 없이 코드가 스스로 프로세스를 셧다운 시키고 재연결하게 만들기 위해 조작합니다.</p>
</li>
</ul>
<hr>
<h1 id="trouble-shooting">Trouble Shooting</h1>
<h3 id="도금-시간이나-검사-전압-범위를-수정하고-싶다">도금 시간이나 검사 전압 범위를 수정하고 싶다</h3>
<ul>
<li><p>조작할 곳: chi(case, filename, ...) 함수 내부의 각 case 블록.</p>
</li>
<li><p>방법: * 도금 시간을 늘리려면 case 1에서 tc = 1200(초)를 원하는 시간으로 늘립니다.</p>
</li>
<li><p>성능 평가 전압 범위를 바꾸려면 case 2나 case 4에서 Ev1, Ev2 값을 수정합니다.</p>
</li>
</ul>
<h3 id="로봇-팔을-특정-비커나-허공으로-바로-빼내고-싶다">로봇 팔을 특정 비커나 허공으로 바로 빼내고 싶다</h3>
<ul>
<li><p>조작할 곳: 스크립트 맨 아래 testrun(robot_id) 함수 또는 별도의 디버깅 스크립트 작성.</p>
</li>
<li><p>방법: * 전체 실험 루프(RobotControlThread)를 주석 처리하고, move_to_tube(1, robot, x좌표, y좌표, 0, 0) 한 줄만 작성해서 실행하면 로봇이 딱 그 위치로만 이동합니다. 하드웨어 캘리브레이션을 검증할 때 필수적인 방법입니다.</p>
</li>
</ul>
<h3 id="실험-레시피-변경">실험 레시피 변경</h3>
<ul>
<li>새로운 혼합 비율을 테스트하라는 지시가 내려왔을 때 수정하는 유일한 곳입니다.</li>
<li>수정 위치: RobotControlThread 클래스 내부의 ratios 변수</li>
</ul>
<h3 id="하드웨어-좌표-영점-조절">하드웨어 좌표 영점 조절</h3>
<ul>
<li><p>수정 위치 1 (기준점): move_to_tube 함수 내부의 좌표 오프셋 수식</p>
<ul>
<li>66.5와 45.5로 적혀 있는 절대 원점(Origin) 숫자를, 로봇을 수동 조작하여 새로 읽어낸 기준점 좌표로 덮어씁니다.</li>
</ul>
</li>
<li><p>수정 위치 2 (높낮이): move_to_tube 호출 시 파라미터로 넘기는 z_height 및 z_down</p>
<ul>
<li>로봇이 비커 바닥을 들이받으면 이 마이너스 값을 더 작게(위로) 수정해야 합니다.</li>
</ul>
</li>
<li><p>수정 위치 3 (작업 순서): electrode_positions.txt, working_positions.txt 파일</p>
<ul>
<li>랙에 꽂힌 쌩 철망의 순서나 비커의 위치를 재배치할 때, 텍스트 파일 안의 [{&quot;x&quot;:0, &quot;y&quot;:0}, ...] 배열 순서를 바꿔줍니다.</li>
</ul>
</li>
</ul>
<h3 id="화학-측정-파라미터-튜닝-어떻게-전기를-쏠-것인가">화학 측정 파라미터 튜닝 (어떻게 전기를 쏠 것인가)</h3>
<p>도금 시간을 늘리거나, 성능 측정 전압의 범위를 변경해야 할 때 수정합니다.</p>
<ul>
<li>수정 위치: chi 함수 내부의 case 1 ~ case 8 블록 및 타임아웃 세팅</li>
<li>수정 방법: 각 레시피 블록 안에 있는 상수들을 요구사항에 맞게 변경합니다. 장비가 자주 뻗는다면 timeouts 배열의 시간(초)을 넉넉하게 늘려줍니다.<pre><code class="language-py">if case == 1:
  # Change &#39;tc&#39; to adjust the electrodeposition time (in seconds)
  tc = 1200 
elif case == 2:
  # Adjust Ev1 and Ev2 to change the voltage sweep range for CV
  Ev1 = 0.3
  Ev2 = 1.2</code></pre>
</li>
</ul>
<h3 id="공정-생략-및-ai-비상-우회-무엇을-건너뛸-것인가">공정 생략 및 AI 비상 우회 (무엇을 건너뛸 것인가)</h3>
<p>시간 단축을 위해 불필요한 검사를 빼거나, 랩실 인터넷이 끊겨서 AI(GPT)가 판독 에러를 뿜으며 시스템을 멈춰 세울 때 응급조치하는 곳입니다.</p>
<p>수정 위치: run_chi_experiments 내부의 메인 루프</p>
<p>수정 방법: * 특정 공정을 빼고 싶다면 해당 move_to_tube(..., chi_case=...) 줄의 맨 앞에 #을 붙여 주석 처리합니다.</p>
<p>AI 통신 장애 시, AI 판독 함수(analyzer.analyze_...)를 주석 처리하고 변수에 1(합격)을 강제 할당하여 무조건 통과시킵니다.</p>
<pre><code class="language-py"># Bypass AI inspection during network errors and force a &#39;Pass&#39; decision
# cp_decision = analyzer.analyze_cp(...) 
cp_decision = {&quot;decision&quot;: 1}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[chi(case) 함수의 8가지 동작 모드 해부]]></title>
            <link>https://velog.io/@houston_guy2/chicase-%ED%95%A8%EC%88%98%EC%9D%98-8%EA%B0%80%EC%A7%80-%EB%8F%99%EC%9E%91-%EB%AA%A8%EB%93%9C-%ED%95%B4%EB%B6%80</link>
            <guid>https://velog.io/@houston_guy2/chicase-%ED%95%A8%EC%88%98%EC%9D%98-8%EA%B0%80%EC%A7%80-%EB%8F%99%EC%9E%91-%EB%AA%A8%EB%93%9C-%ED%95%B4%EB%B6%80</guid>
            <pubDate>Sun, 26 Apr 2026 02:26:10 GMT</pubDate>
            <description><![CDATA[<p>3번(검사) ➡️ 1번(제조) ➡️ 4번/7번(에이징) ➡️ 2번/8번</p>
<h2 id="1단계-라인-연결-테스트-단선-검사">1단계: 라인 연결 테스트 (단선 검사)</h2>
<p>사용 케이스: Case 3 (EIS)</p>
<p>언제 쓰는가: 로봇 팔이 쌩 철망을 집어서 용액 비커에 방금 막 담갔을 때.</p>
<p>물리적 의미: 메인 전력을 쏘기 전에, 집게가 철망을 헐렁하게 물지는 않았는지 약한 교류 신호(Ping)를 보내 회로의 물리적 연결 상태를 점검합니다.</p>
<h2 id="2단계-메인-제조-공정-도금">2단계: 메인 제조 공정 (도금)</h2>
<p>사용 케이스: Case 1 (CP)</p>
<p>언제 쓰는가: 1단계에서 단선이 아님(정상 연결)이 확인되었을 때.</p>
<p>물리적 의미: 정전류(Constant Current)를 장시간 쏴서 철망 겉면에 타겟 물질(NiMoP)을 강제로 달라붙게 만드는 실질적인 제조 공정입니다.</p>
<h2 id="3단계-준비-운동-및-세척-에이징">3단계: 준비 운동 및 세척 (에이징)</h2>
<p>사용 케이스: Case 4, Case 7 (CV 10회 반복)</p>
<p>언제 쓰는가: 도금이 끝난 전극을 최종 성능 테스트용 비커(KOH 용액)로 옮겨 담은 직후.</p>
<p>물리적 의미: 갓 구워낸 촉매는 표면이 매우 불안정합니다. 전압을 위아래로 빠르게 여러 번 흔들어주어 표면의 불순물을 날려버리고 전기화학적으로 길들이는(Conditioning) 단계입니다.</p>
<h2 id="4단계-최종-성능-벤치마크-데이터-추출">4단계: 최종 성능 벤치마크 (데이터 추출)</h2>
<p>사용 케이스: Case 2, Case 8 (CV 2회 반복)</p>
<p>언제 쓰는가: 3단계의 워밍업이 완전히 끝나고 촉매 상태가 안정화되었을 때.</p>
<h2 id="5단계-극한-스트레스-테스트-내구성">5단계: 극한 스트레스 테스트 (내구성)</h2>
<p>사용 케이스: Case 5 (CA)</p>
<p>언제 쓰는가: 성능 평가까지 무사히 마친 전극이 장시간 구동에도 버티는지 확인할 때.</p>
<p>물리적 의미: 특정 전압을 장시간(예: 20분) 가혹하게 걸어놓고, 수소 기포가 터지는 충격에 코팅된 촉매가 떨어져 나가지 않는지 확인하는 내구성 검증입니다.</p>
<p>물리적 의미: 교수님이나 보고서에 제출할 진짜 수소 발생 성능 데이터를 정밀하게 측정해서 그래프로 뽑아내는 핵심 평가 단계입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[기본 코드지식]]></title>
            <link>https://velog.io/@houston_guy2/%EA%B8%B0%EB%B3%B8-%EC%BD%94%EB%93%9C%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@houston_guy2/%EA%B8%B0%EB%B3%B8-%EC%BD%94%EB%93%9C%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Sat, 25 Apr 2026 07:17:23 GMT</pubDate>
            <description><![CDATA[<h1 id="자동화-제어-파이썬-코드의-기본-구조">자동화 제어 파이썬 코드의 기본 구조</h1>
<pre><code class="language-py">import serial
import time

# Initialize serial connections for robot and potentiostat
robot_port = serial.Serial(&#39;COM3&#39;, baudrate=9600, timeout=1)
potentiostat_port = serial.Serial(&#39;COM4&#39;, baudrate=115200, timeout=1)

def move_robot(x, y, z):
    # Format command string (e.g., G-code) to send to the robotic arm controller
    command = f&quot;G0 X{x} Y{y} Z{z}\r\n&quot;
    robot_port.write(command.encode(&#39;utf-8&#39;))

    # Polling loop: Wait for &#39;OK&#39; acknowledgment from the robot (similar to checking a valid flag in RTL)
    while True:
        response = robot_port.readline().decode(&#39;utf-8&#39;).strip()
        if response == &quot;OK&quot;:
            break
        time.sleep(0.1)

def run_electrodeposition(voltage, duration):
    # Send command to potentiostat to apply bias
    command = f&quot;SET_V {voltage}\r\nSTART {duration}\r\n&quot;
    potentiostat_port.write(command.encode(&#39;utf-8&#39;))

    # Wait for the hardware to complete the reaction
    time.sleep(duration)

# Main execution loop simulating High-throughput screening
# Array of coordinates corresponding to different beakers (Ternary plot mixtures)
test_coordinates = [(10, 20, 5), (30, 20, 5), (50, 20, 5)]

for target_pos in test_coordinates:
    try:
        # State 1: Move robot arm over the specific beaker
        move_robot(target_pos[0], target_pos[1], target_pos[2])

        # State 2: Lower the arm (electrode) into the electrolyte solution
        move_robot(target_pos[0], target_pos[1], target_pos[2] - 10)

        # State 3: Trigger the Potentiostat to start coating for 60 seconds
        run_electrodeposition(-1.0, 60)

        # State 4: Pull the arm back up
        move_robot(target_pos[0], target_pos[1], target_pos[2])

    except Exception as e:
        # Exception handling is critical to prevent hardware collision or acid spills
        print(f&quot;Hardware error occurred at {target_pos}: {e}&quot;)
        # Send emergency stop command
        robot_port.write(b&quot;E-STOP\r\n&quot;)
        break</code></pre>
<hr>
<ol>
<li><p>Communication Specs: What are the physical interfaces (UART, TCP/IP) and specific port settings (baud rate, COM port) connecting the main PC to the robot and potentiostat?</p>
</li>
<li><p>API &amp; Manuals: Where can I find the original manufacturer APIs, DLLs, or raw command set manuals (e.g., G-code) for these hardware devices?</p>
</li>
<li><p>Synchronization: How does the Python script verify that a hardware movement or chemical process is finished—does it wait for a hardware ACK signal, or does it just use time.sleep()?</p>
</li>
<li><p>Exception Handling: What is the software-level fail-safe or E-stop routine if a communication timeout or physical collision occurs while the voltage is active?</p>
</li>
<li><p>Environment Setup: How are the Python dependencies managed (e.g., virtual environment, requirements.txt) to ensure this code runs correctly if we need to set up a new PC?</p>
</li>
</ol>
<hr>
<h3 id="1-메인-pc와-각-장비로봇-팔-전위차계-간의-물리적-인터페이스와-통신-스펙은-어떻게-됩니까">1. &quot;메인 PC와 각 장비(로봇 팔, 전위차계) 간의 물리적 인터페이스와 통신 스펙은 어떻게 됩니까?&quot;</h3>
<p>의도: 가장 기본적이지만 놓치기 쉬운 하드웨어 레이어 파악입니다.</p>
<p>세부 질문: &quot;단순 USB-to-Serial(UART) 인가요, 아니면 TCP/IP(이더넷) 통신인가요? 시리얼이라면 포트 번호(COM port) 매칭 규칙이나 통신 속도(Baudrate) 설정값은 어디서 확인합니까?&quot;</p>
<h3 id="2-각-장비의-원시-명령어-세트raw-command-set-매뉴얼이나-제조사-api-문서는-어디에-저장되어-있습니까">2. &quot;각 장비의 원시 명령어 세트(Raw Command Set) 매뉴얼이나 제조사 API 문서는 어디에 저장되어 있습니까?&quot;</h3>
<p>의도: 코드에 하드코딩된 f&quot;G0 X{x}&quot; 같은 문자열들이 도대체 어디서 튀어나온 규칙인지 원본 데이터시트를 확보해야 합니다. 하드웨어 개발 시 &#39;레지스터 맵(Register Map)&#39;을 확보하는 것과 똑같습니다.</p>
<p>세부 질문: &quot;로봇 팔 제조사에서 제공하는 G-code 리스트나, 전위차계를 제어하기 위해 불러다 쓰는 DLL/라이브러리 매뉴얼 PDF 파일이 폴더 어디에 있습니까?&quot;</p>
<h3 id="3-로봇-팔-이동-완료나-도금-공정-종료를-pc가-어떤-방식타이밍으로-인지하고-다음-시퀀스로-넘어갑니까">3. &quot;로봇 팔 이동 완료나 도금 공정 종료를 PC가 어떤 방식(타이밍)으로 인지하고 다음 시퀀스로 넘어갑니까?&quot;</h3>
<p>의도: 시스템 병목과 딜레이가 가장 많이 터지는 지점인 &#39;동기화(Synchronization)&#39; 방식을 파악합니다.</p>
<p>세부 질문: &quot;로봇 팔이 지정된 위치로 이동할 때까지 파이썬 코드는 그냥 time.sleep()으로 무식하게 기다립니까(Delay 기반), 아니면 로봇 컨트롤러에서 이동이 끝났다는 &#39;ACK(응답)&#39; 신호를 쏴주고 그걸 받아서 넘어갑니까(Polling/Interrupt 기반)?&quot;</p>
<h3 id="4-장비-간-통신-타임아웃이-발생하거나-충돌collision이-났을-때-비상-정지e-stop나-복구recovery-루틴은-어떻게-구현되어-있습니까">4. &quot;장비 간 통신 타임아웃이 발생하거나 충돌(Collision)이 났을 때, 비상 정지(E-Stop)나 복구(Recovery) 루틴은 어떻게 구현되어 있습니까?&quot;</h3>
<p>의도: 실제 랩실에서 에러가 터졌을 때 수습하기 위한 안전장치 파악입니다.</p>
<p>세부 질문: &quot;도금 중에 파이썬 통신이 뻗어버리면, 전위차계가 전압 인가를 스스로 멈춥니까 아니면 계속 전류를 쏴서 시료를 태워 먹습니까? 물리적인 비상 정지 스위치 외에 소프트웨어적인 안전망이 코드 어디에 있습니까?&quot;</p>
<h3 id="5-현재-이-파이썬-코드가-돌아가는-패키지-환경dependencies은-어떻게-관리되고-있습니까">5. &quot;현재 이 파이썬 코드가 돌아가는 패키지 환경(Dependencies)은 어떻게 관리되고 있습니까?&quot;</h3>
<p>의도: 나중에 PC를 포맷하거나 다른 노트북에서 셋업할 때, 버전 충돌로 코드가 아예 안 돌아가는 참사를 막기 위함입니다.</p>
<p>세부 질문: &quot;가상 환경(conda, venv) 세팅이 되어 있습니까? requirements.txt처럼 이 코드를 돌리기 위해 설치해야 하는 특정 버전의 라이브러리 목록이 정리된 파일이 있습니까?&quot;</p>
<hr>
<h1 id="pyserial-모듈-시리얼-통신">pyserial 모듈 (시리얼 통신)</h1>
<pre><code class="language-py">import serial
import time

# 1. Initialize UART connection
try:
    # Open COM4 with 115200 baudrate and 2 seconds timeout
    robot_uart = serial.Serial(port=&#39;COM4&#39;, baudrate=115200, timeout=2.0)
except serial.SerialException:
    # Exit if COM port is already in use or not found
    print(&quot;Error: Could not open the serial port.&quot;)
    exit()

def send_command_and_wait(command_str):
    # 2. Transmit (TX)
    # Append CR/LF to the command and convert string to bytes
    tx_data = (command_str + &quot;\r\n&quot;).encode(&#39;utf-8&#39;)
    robot_uart.write(tx_data)

    # 3. Receive (RX)
    # Wait and read from RX buffer until &#39;\n&#39; is detected or timeout occurs
    rx_bytes = robot_uart.readline()

    # Check if timeout occurred (empty byte received)
    if len(rx_bytes) == 0:
        return &quot;TIMEOUT_ERROR&quot;

    # Convert received bytes back to string and remove trailing &#39;\r\n&#39;
    response = rx_bytes.decode(&#39;utf-8&#39;).strip()
    return response

# Execution sequence
# Send a G-code command to move the robotic arm
print(&quot;Sending move command...&quot;)
result = send_command_and_wait(&quot;G0 X10 Y20 Z5&quot;)

# Check the ACK signal from the hardware controller
if result == &quot;OK&quot;:
    print(&quot;Robot reached the target position successfully.&quot;)
elif result == &quot;TIMEOUT_ERROR&quot;:
    print(&quot;Critical: Hardware did not respond within 2 seconds. Check connection.&quot;)
else:
    print(f&quot;Hardware returned an unexpected error code: {result}&quot;)

# Always close the port when done
robot_uart.close()</code></pre>
<h2 id="serialserial">serial.Serial()</h2>
<pre><code class="language-py">robot_uart = serial.Serial(port=&#39;COM4&#39;, baudrate=115200, timeout=2.0)</code></pre>
<p>: 하드웨어 연결 및 UART 세팅
C언어에서 포트를 열고 Baudrate, Parity, Stop bit 레지스터를 세팅하는 과정입니다. 파이썬에서는 객체(Object)를 하나 생성하는 것으로 이 과정을 끝냅니다.</p>
<p>동작: 지정된 COM 포트를 점유하고, 통신 규격을 맞춘 뒤 연결을 엽니다.</p>
<ul>
<li><p>port: 장치가 연결된 포트 이름 (윈도우는 &#39;COM3&#39;, 리눅스는 &#39;/dev/ttyUSB0&#39; 등).</p>
</li>
<li><p>baudrate: 통신 속도 (9600, 115200 등). 하드웨어 메뉴얼과 반드시 일치해야 합니다.</p>
</li>
<li><p>timeout: (가장 중요) 수신 대기 시간(초)입니다. C언어처럼 무한 while 루프로 RX 버퍼를 폴링(Polling)하면 CPU 점유율이 폭주하므로, 파이썬에서는 응답이 안 오면 일정 시간 후 루프를 빠져나오도록 반드시 타임아웃을 설정해야 합니다.</p>
</li>
</ul>
<h2 id="g0은-항상-고정인가">G0은 항상 고정인가?</h2>
<p>G0은 파이썬과 아무런 상관이 없습니다. 프린터, CNC 기계, 랩실용 3축 로봇 팔 등을 제어할 때 전 세계 산업 표준으로 쓰이는 <strong>G-코드(G-code)</strong>라는 하드웨어 제어 언어(명령어 세트) 중 하나입니다.</p>
<p>G0 (Rapid Move, 급속 이송): * 의미: &quot;경로 신경 쓰지 말고, 모터가 낼 수 있는 최고 속도로 무조건 빨리 그 좌표로 가라.&quot;</p>
<ul>
<li>사용처: 비커와 비커 사이를 허공에서 이동할 때 시간을 단축하기 위해 씁니다.</li>
</ul>
<p>G1 (Linear Move, 직선 보간 이동):</p>
<ul>
<li>의미: &quot;내가 지정해 준 속도(Feedrate, F)로 경로를 일정하게 유지하면서 이동해라.&quot;</li>
<li>사용처: 촉매가 발린 전극을 산성 용액(비커) 안으로 담글 때 씁니다. 만약 용액에 담글 때 G0을 써버리면, 전극이 용액을 강하게 내리쳐서 비커가 깨지거나 산성 용액이 사방으로 튀는 대형 사고가 납니다.</li>
</ul>
<h2 id="write">.write()</h2>
<pre><code class="language-py"># 2. Transmit (TX)
    # Append CR/LF to the command and convert string to bytes
    tx_data = (command_str + &quot;\r\n&quot;).encode(&#39;utf-8&#39;)
    robot_uart.write(tx_data)</code></pre>
<p>: TX (데이터 송신)
PC에서 로봇 팔이나 계측기로 명령(Command)을 쏘는 함수입니다. TX 버퍼에 데이터를 밀어 넣습니다.</p>
<ul>
<li>주의점 (인코딩): 파이썬의 문자열(String)은 그대로 케이블을 탈 수 없습니다. 반드시 바이트(Byte) 형태로 쪼개서 직렬화해야 합니다.</li>
</ul>
<ul>
<li><p><span style="color:orange">방법: 전송할 문자열 뒤에 .encode(&#39;utf-8&#39;)을 붙이거나, 문자열 앞에 b를 붙여야 합니다 (예: b&quot;START&quot;).</span></p>
</li>
<li><p><span style="color:orange">종결 문자: 하드웨어(장비)가 &quot;아, 명령어가 끝났구나&quot;라고 인식하게 하려면 끝에 항상 캐리지 리턴(\r)이나 라인 피드(\n)를 붙여서 보내야 합니다.</span></p>
</li>
</ul>
<h2 id="readline">.readline()</h2>
<pre><code class="language-py"># 3. Receive (RX)
    rx_bytes = robot_uart.readline()

    response = rx_bytes.decode(&#39;utf-8&#39;).strip()
    return response</code></pre>
<p>: RX (데이터 수신)
장비에서 PC로 들어온 응답(ACK 신호나 센서 측정값)을 읽어오는 함수입니다. RX 버퍼를 확인합니다.</p>
<p>동작: 수신 버퍼에 쌓인 데이터 중 개행 문자(\n)가 나올 때까지 한 줄을 통째로 읽어옵니다.</p>
<p>주의점 (디코딩 및 클렌징): 들어온 데이터는 바이트 덩어리이므로, 사람이 읽고 변수로 쓰려면 다시 .decode(&#39;utf-8&#39;)을 통해 문자열로 풀어줘야 합니다.</p>
<p>또한, 하드웨어가 보낸 데이터 끝에는 쓰레기값이나 줄바꿈 문자(\r\n)가 붙어 있으므로, 문자열 앞뒤 공백을 날려버리는 .strip() 함수를 세트로 붙여 쓰는 것이 국룰입니다.</p>
<h1 id="formatted-string-f-string과-인코딩-encodedecode">Formatted String (f-string)과 인코딩 (.encode/.decode)</h1>
<h2 id="f-string-문자열-포매팅">f-string (문자열 포매팅)</h2>
<p>: C언어의 sprintf 대체
f는 단순히 string으로 변환한다는 뜻이 아니라, <strong>&quot;이 문자열 안에 있는 중괄호 {} 속 변수들을 실제 값으로 치환해서 문자열을 완성해라(Format)&quot;</strong>라는 파이썬의 특수 문법입니다.</p>
<pre><code class="language-py"># Variables representing coordinates
target_x = 100
target_y = 50

# Using f-string to format the command
# This replaces C language&#39;s: sprintf(buffer, &quot;G0 X%d Y%d\r\n&quot;, target_x, target_y);
command_str = f&quot;G0 X{target_x} Y{target_y}\r\n&quot;

print(command_str) 
# Output: G0 X100 Y50\r\n (A single human-readable string object)</code></pre>
<p>로봇 팔을 제어하려면 X: 10, Y: 20이라는 좌표 변수값을 G0 X10 Y20이라는 하나의 문자열(명령어)로 조립해야 합니다. 과거 파이썬이나 다른 언어에서는 변수와 문자열을 + 기호로 더하거나 복잡한 포맷 기호를 썼지만, 현재는 f-string이 표준입니다.</p>
<p>사용법: 문자열을 여는 따옴표 앞에 소문자 f를 붙이고, 변수를 중괄호 {} 안에 넣으면 끝입니다.</p>
<h2 id="encode-송신---tx-string-➡️-bytes">.encode() (송신 - TX): String ➡️ Bytes</h2>
<pre><code class="language-py"># Encode the human-readable string into a byte array
# &#39;utf-8&#39; translates each character into its corresponding hex/binary value
tx_data = command_str.encode(&#39;utf-8&#39;)

print(tx_data)
# Output: b&#39;G0 X100 Y50\r\n&#39; (The &#39;b&#39; prefix indicates it is now raw byte data)

# Send to hardware
# robot_uart.write(tx_data)</code></pre>
<p>앞서 f-string으로 만든 command_str은 파이썬 내부에서만 의미가 있는 &#39;문자열 객체&#39;입니다. 이 객체를 UART 케이블 구리선에 그대로 태워 보낼 수는 없습니다. 반드시 하드웨어가 인식할 수 있는 순수한 8비트 데이터 배열(Raw Bytes)로 쪼개야 합니다.</p>
<p>원리: 문자를 ASCII 코드(또는 UTF-8 규칙) 숫자로 변환하는 과정입니다.</p>
<p>사용법: 문자열 뒤에 .encode(&#39;utf-8&#39;) 또는 .encode(&#39;ascii&#39;)를 붙입니다. 산업용 장비 제어에서는 두 방식의 결과가 사실상 동일합니다.</p>
<h2 id="decode-수신---rx">.decode() (수신 - RX)</h2>
<p>: Bytes ➡️ String</p>
<pre><code class="language-py"># Simulated raw bytes received from hardware via serial port
# rx_data = robot_uart.readline()
rx_data = b&#39;OK\r\n&#39; 

# Decode bytes back to string and remove trailing whitespace/newlines
response_str = rx_data.decode(&#39;utf-8&#39;).strip()

print(response_str)
# Output: OK (Clean string, ready for conditional statements)

if response_str == &quot;OK&quot;:
    # Execute next sequence
    pass</code></pre>
<p>로봇 팔이 이동을 완료하고 PC로 &quot;OK&quot;라는 신호를 보냈다고 가정해 보겠습니다. 케이블을 타고 PC로 들어온 데이터 역시 문자열이 아니라 순수한 바이트(Bytes) 배열입니다. 이를 if response == &quot;OK&quot;:처럼 조건문에서 비교하려면 다시 사람이 읽을 수 있는 텍스트로 번역해야 합니다.</p>
<ul>
<li><p>원리: 수신된 ASCII 코드 숫자 배열을 다시 문자로 조립하는 과정입니다.</p>
</li>
<li><p>사용법: 바이트 데이터 뒤에 .decode(&#39;utf-8&#39;)을 붙입니다.</p>
</li>
<li><p>.strip()의 중요성: 장비가 데이터를 보낼 때 끝에 항상 개행 문자(\r, \n)나 공백이 붙어 옵니다. 이를 그대로 두면 &quot;OK&quot;와 &quot;OK\r\n&quot;이 다르다고 판정되어 코드가 꼬입니다. 따라서 디코드 직후 꼬리표를 잘라내는 .strip()을 무조건 세트로 붙여야 합니다.</p>
</li>
</ul>
<p>데이터의 흐름 (Pipeline):
변수 ➡️ f-string (조립) ➡️ .encode() (바이트 변환) ➡️ UART TX ➡️ 장비 동작 ➡️ UART RX ➡️ .decode() (문자 변환) ➡️ .strip() (쓰레기값 제거) ➡️ 제어 흐름 결정</p>
<h1 id="예외-처리-try-except-블록">예외 처리 (try-except 블록):</h1>
<h2 id="try-except의-기본-구조와-동작-원리">try-except의 기본 구조와 동작 원리</h2>
<pre><code class="language-py">import serial
import time

robot_port = serial.Serial(&#39;COM3&#39;, 115200, timeout=1)
potentiostat_port = serial.Serial(&#39;COM4&#39;, 9600, timeout=1)

def run_experiment(x, y, z):
    # try block: Attempting the risky sequence of operations
    try:
        # 1. Move robot to target position
        move_command = f&quot;G0 X{x} Y{y} Z{z}\r\n&quot;.encode(&#39;utf-8&#39;)
        robot_port.write(move_command)

        # 2. Apply voltage for electrodeposition
        potentiostat_port.write(b&quot;SET_V -1.0\r\n&quot;)
        time.sleep(10) # Wait for coating process

    # except block: Handle specific hardware communication errors (e.g., cable disconnected)
    except serial.SerialException as e:
        print(f&quot;Hardware connection lost: {e}&quot;)
        # Send physical Emergency Stop signal if possible
        robot_port.write(b&quot;E-STOP\r\n&quot;)
        potentiostat_port.write(b&quot;V_OFF\r\n&quot;)

    # except block: Catch any other unexpected software errors
    except Exception as e:
        print(f&quot;Unexpected software error occurred: {e}&quot;)
        # Ensure voltage is turned off to prevent burning the sample
        potentiostat_port.write(b&quot;V_OFF\r\n&quot;)

    # finally block: This runs no matter what (success or failure)
    finally:
        print(&quot;Experiment sequence ended. Safely closing ports.&quot;)
        # Ensure ports are freed so they can be used again without rebooting the PC
        if robot_port.is_open:
            robot_port.close()
        if potentiostat_port.is_open:
            potentiostat_port.close()

# Start the process
run_experiment(10, 20, 5)</code></pre>
<p>소프트웨어는 실행 중에 통신 케이블이 뽑히거나, 범위를 벗어난 값이 들어오면 즉시 &#39;크래시(Crash)&#39;가 나면서 스크립트가 강제 종료됩니다. 파이썬에서는 이를 &#39;예외(Exception)가 발생했다&#39;고 표현합니다.</p>
<p>try-except는 이 폭탄이 터지는 것을 중간에 가로채서(Catch), 프로그램이 죽는 대신 안전하게 수습할 수 있는 기회를 줍니다.</p>
<ul>
<li><p>try 블록: &quot;일단 이 코드를 실행해 봐. (하지만 하드웨어 통신이라 에러가 날 확률이 있어.)&quot;</p>
</li>
<li><p>except 블록: &quot;만약 위에서 에러가 터지면, 스크립트를 죽이지 말고 여기로 점프해서 수습해!&quot;</p>
</li>
<li><p>finally 블록 (옵션): &quot;에러가 나든 안 나든, 맨 마지막에 이건 무조건 실행해. (예: 포트 닫기, 전압 차단)&quot;</p>
</li>
</ul>
<h1 id="robot-arm-control">Robot Arm Control</h1>
<h2 id="1-safe-z-retraction">1. Safe Z Retraction</h2>
<p>가장 빈번하게 발생하는 사고 원인입니다. 로봇 팔이 A 비커에서 B 비커로 이동할 때, 현재 위치에서 목표 위치로 직선(대각선) 이동을 해버리면 비커 벽면을 다 부수고 지나가게 됩니다.</p>
<p>이를 방지하기 위해 코드는 반드시 &#39;ㄷ&#39;자 형태의 시퀀스를 가져야 합니다.</p>
<pre><code class="language-py">def safe_move_to_beaker(target_x, target_y, target_z):
    # 1. Z-axis Retract: Pull the electrode completely out of the current beaker
    # Moving to a pre-defined &#39;SAFE_Z&#39; height where there are no obstacles
    send_command(f&quot;G0 Z{SAFE_Z_HEIGHT}&quot;)
    wait_for_idle()

    # 2. XY Plane Move: Move laterally to the target beaker&#39;s coordinates
    send_command(f&quot;G0 X{target_x} Y{target_y}&quot;)
    wait_for_idle()

    # 3. Z-axis Plunge: Lower the electrode into the new solution
    send_command(f&quot;G0 Z{target_z}&quot;)
    wait_for_idle()</code></pre>
<h2 id="2-절대-좌표-vs-상대-좌표-absolute-vs-relative-positioning">2. 절대 좌표 vs 상대 좌표 (Absolute vs. Relative Positioning)</h2>
<p>모터 제어 명령어가 절대 좌표(Absolute, G90) 기준인지, 상대 좌표(Relative, G91) 기준인지 명확히 파악해야 합니다.
마이크로프로세서에서 메모리 &#39;절대 주소&#39;에 값을 쓸 것인지, 현재 포인터 위치에서 &#39;오프셋(Offset)&#39;만큼 이동할 것인지의 차이와 같습니다.</p>
<p>절대 좌표 (예: X=10): 현재 위치가 어디든 상관없이, 작업대(Table)의 기준점으로부터 10cm 떨어진 고정 위치로 이동합니다. (랩실 자동화에서 주로 쓰임)</p>
<p>상대 좌표 (예: X=+10): 현재 로봇 팔이 있는 위치에서 우측으로 10cm 더 이동합니다.</p>
<p>내일 확인할 것: 코드가 어떤 모드로 세팅되어 돌아가는지 확인해야 합니다. 상대 좌표 모드에서 코드를 잘못 짜면 루프를 돌 때마다 위치가 누적되어 로봇 팔이 작업대 밖으로 탈선하게 됩니다.</p>
<h2 id="3-호밍과-원점-복귀-homing--calibration">3. 호밍과 원점 복귀 (Homing / Calibration)</h2>
<p>로봇 팔의 전원을 방금 켰다고 가정해 보겠습니다. 컨트롤러는 자기 자신이 3차원 공간의 어디에 있는지 알지 못합니다.
디지털 회로에서 전원을 켜자마자 플립플롭들을 0으로 초기화하기 위해 리셋(Reset) 신호를 때리는 것과 완벽히 같은 개념입니다.</p>
<p>동작 원리: 로봇 팔이 각 축(X, Y, Z)의 끝에 달린 물리적인 <strong>리미트 스위치(Limit Switch)</strong>를 &#39;딸깍&#39;하고 칠 때까지 천천히 이동합니다. 스위치가 눌리는 순간을 공간의 원점(0, 0, 0)으로 인식하고 내부 카운터를 초기화합니다.</p>
<p>내일 확인할 것: &quot;파이썬 스크립트를 처음 실행할 때, 장비가 자동으로 호밍(Homing) 루틴을 수행하는지, 아니면 수동으로 특정 명령을 먼저 쏴줘야 하는지&quot;를 물어봐야 합니다</p>
<h2 id="4-소프트웨어-리미트-software-limits--bounding-box">4. 소프트웨어 리미트 (Software Limits / Bounding Box)</h2>
<p>물리적인 스위치 외에도, 파이썬 코드 단에서 로봇이 지정된 작업 공간(Workspace) 밖으로 나가지 못하게 막는 <strong>&#39;안전 울타리(Bounding Box)&#39;</strong>가 있어야 합니다.</p>
<pre><code class="language-py"># Hardware workspace physical limits defined in millimeters
MAX_X = 500
MAX_Y = 300
MIN_Z = 0

def move_robot(x, y, z):
    # Software limit check before sending the command to the hardware
    if x &gt; MAX_X or y &gt; MAX_Y or z &lt; MIN_Z:
        print(&quot;Error: Target coordinates are outside the safe bounding box!&quot;)
        return False # Cancel the movement

    # Proceed with sending UART command if safe
    send_command(f&quot;G0 X{x} Y{y} Z{z}&quot;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[배경지식]]></title>
            <link>https://velog.io/@houston_guy2/%EB%B0%B0%EA%B2%BD%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@houston_guy2/%EB%B0%B0%EA%B2%BD%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Sat, 25 Apr 2026 06:52:17 GMT</pubDate>
            <description><![CDATA[<h1 id="수소연료-기본지식">수소연료 기본지식</h1>
<p>수소 = 궁극의 &#39;무한 용량 하드디스크&#39;
여기서 수소(H2)가 등장합니다. 수소를 자체적인 연료라기보다는 <strong>&#39;남는 전기를 보관하는 화학적 에너지 저장 매체(Energy Carrier)&#39;</strong>로 이해하셔야 합니다.</p>
<ul>
<li><p>Write (저장): 낮에 남아도는 잉여 전력으로 물(H2O)을 전기분해합니다. 이때 전기에너지가 수소 가스라는 &#39;화학 에너지&#39; 형태로 변환되어 저장됩니다. (이것이 랩실에서 하는 Water splitting입니다.)</p>
</li>
<li><p>Store (보관): 만들어진 수소를 탱크에 압축해서 보관합니다. 배터리와 달리 방전되지 않으며, 몇 달이고 보관할 수 있습니다.</p>
</li>
<li><p>Read (출력): 전기가 필요한 밤이나 겨울에, 보관해둔 수소를 연료전지(Fuel Cell)에 넣고 산소와 결합시킵니다. 그러면 물이 배출되면서 다시 &#39;전기&#39;가 나옵니다.</p>
</li>
</ul>
<p>이론상으로는 완벽하지만, 현실에서는 아직 수소 경제가 화석 연료를 대체하지 못하고 있습니다. 이유는 단순합니다. 너무 비효율적이고 비싸기 때문입니다.</p>
<p>물을 분해해서 수소를 만들 때 전기가 너무 많이 소모되고, 장치에 백금이나 이리듐 같은 극도로 비싼 귀금속 촉매를 발라야만 기계가 굴러갑니다. 배보다 배꼽이 더 큰 상황입니다.</p>
<p>그래서 교수님 연구실은 비싼 백금 대신 <strong>&#39;싸구려 철망(SS316)&#39;</strong>에 <strong>&#39;저렴한 화합물(NiMoP)&#39;</strong>을 코팅해서 수소를 싸게 대량 생산할 수 있는 방법을 찾고 있는 것입니다. 이 코팅의 최적 조건을 사람이 일일이 찾기 어려우니, 로봇과 제어 시스템을 이용한 &#39;고효율 자동화 시스템(High-throughput)&#39;으로 수백 번의 실험을 돌려버리는 것이 이 포스터의 핵심 결론입니다.</p>
<hr>
<h1 id="raman-spectroscopy-라만분석">Raman Spectroscopy 라만분석</h1>
<p>라만 분석은 한마디로 <strong>&quot;빛을 쏴서 물질의 고유한 &#39;지문(Fingerprint)&#39;을 채취하는 기술&quot;</strong>입니다.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d0017d1f-a3cd-47fc-9c31-9af5dcb7a8cf/image.png" alt=""></p>
<p>메아리를 상상해 보면 이해하기 쉽습니다. 동굴에 대고 특정 주파수의 소리(야호)를 질렀을 때, 동굴 벽의 재질이나 구조에 따라 돌아오는 메아리의 음정이 아주 미세하게 바뀌어 돌아오는 현상과 비슷합니다.</p>
<p>레이저 발사 (Input): 분석하고 싶은 물질에 단일 주파수(한 가지 색깔)의 강한 레이저 빛을 쏩니다.</p>
<p>빛의 충돌과 튕김 (Scattering): 빛이 물질을 이루는 분자들에 부딪힌 후 사방으로 튕겨 나갑니다(산란).</p>
<p>주파수 변화 (Raman Shift): 튕겨 나오는 빛의 99.999%는 원래 쐈던 레이저와 똑같은 주파수(색깔)로 돌아옵니다. 하지만 아주 극소수(약 1천만 분의 1)의 빛은 물질 내부 분자의 고유한 진동과 부딪히면서, 에너지를 뺏기거나 얻어 주파수가 미세하게 변한 상태로 튕겨 나옵니다.</p>
<hr>
<h1 id="words">Words</h1>
<ul>
<li>전해증착: Electrodeposition</li>
<li>전해질 / 용액: Electrolyte</li>
<li>물 분해 (수전해): Water splitting</li>
<li>기판: Substrate</li>
<li>과전압: Overpotential. 원하는 반응을 일으키기 위해 이론치보다 추가로 더 밀어 넣어줘야 하는 전압입니다. 이 값이 낮을수록 &quot;효율이 좋은 촉매&quot;로 평가받습니다.</li>
<li>경쟁 반응: Competing reaction</li>
<li>침전물 / 수산화물: Precipitate / Hydroxides</li>
</ul>
<h3 id="aem">AEM</h3>
<p>Anion Exchange Membrane (음이온 교환 막)
<img src="https://velog.velcdn.com/images/houston_guy2/post/3c0745fe-a34b-492f-bf28-2bd111da0dbc/image.png" alt=""></p>
<ul>
<li><p>수전해 하드웨어 시스템의 (+)극과 (-)극 사이에는 용액과 가스가 마구 섞이지 않도록 막(Membrane)을 설치합니다. AEM은 이름 그대로 음이온(Anion), 그중에서도 특히 <strong>수산화 이온(OH⁻)</strong>만 통과시킵니다. 이 막이 있어야 양쪽 전극에서 발생하는 수소(H₂)와 산소(O₂) 가스가 섞여 폭발하는 것을 막고 순수하게 분리해서 포집할 수 있습니다.</p>
</li>
<li><p>AEM은 &#39;알칼리성(Alkaline, 염기성)&#39; 환경에서 작동합니다. 알칼리성 환경에서는 일반 금속도 부식에 훨씬 잘 버팁니다.</p>
</li>
<li><p>이것이 바로 포스터 2번 섹션에서 비싼 소재 대신 <strong>값싼 스테인리스 스틸 철망(SS316 mesh)</strong>을 전극으로 쓰고, 백금 대신 저렴한 NiMoP(니켈-몰리브덴-인) 촉매를 개발하려 애쓰는 근본적인 이유입니다. AEM 기반의 알칼리성 환경이 전제되어 있기 때문에 &quot;비용 효율적인 수소 생산(Cost-effective green hydrogen production)&quot;이 가능해지는 것입니다.</p>
</li>
</ul>
<hr>
<h1 id="2-electrode-system--3-electrode-system">2-Electrode System / 3-Electrode System</h1>
<h2 id="2-electrode-system-2전극-시스템">2-Electrode System (2전극 시스템)</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/fb4a5f7e-8cc4-4091-892d-0d75cc84bfce/image.png" alt=""></p>
<p>: &quot;전체 배터리 시스템 테스트&quot;
가장 단순한 폐회로(Closed Loop)입니다. (+)극과 (-)극, 딱 두 개의 선만 용액에 담급니다.
포스터의 2번 섹션에 있는 전해조(Electrolyzer) 패키징이 바로 전형적인 2전극 시스템입니다.</p>
<ul>
<li><p>구성: Anode(양극)와 Cathode(음극).</p>
</li>
<li><p>회로적 의미: Power Supply에서 전압을 인가하면 전류가 Anode로 들어가서 용액을 거쳐 Cathode로 나옵니다.</p>
</li>
<li><p>치명적인 문제 (왜 이걸로 분석을 못 하는가?): 장비 화면에 &quot;전압 2V, 전류 1A&quot;라고 떴다고 가정해 봅시다. 이 2V는 우리가 테스트하려는 철망(Target)에만 걸린 전압이 아닙니다.</p>
<ul>
<li>타겟 전극의 저항</li>
<li>반대쪽 전극의 저항</li>
<li>용액 자체의 저항 ($V = IR$ drop)</li>
</ul>
<p>이 세 가지가 직렬로 연결된 회로의 <strong>&#39;전체 양단 전압&#39;</strong>입니다. 즉, 내가 만든 철망 촉매가 성능이 좋은 건지, 아니면 반대쪽 전극 성능이 좋은 건지 구분이 안 됩니다. 노이즈와 오차가 너무 큽니다.</p>
</li>
</ul>
<h2 id="3-electrode-system-3전극-시스템">3-Electrode System (3전극 시스템)</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/719ce4d8-d6b9-485b-8293-d38f74292500/image.png" alt=""></p>
<p>: &quot;DUT 정밀 소자 분석&quot;
이게 바로 포스터 1번, 3번, 4번 섹션에서 전극의 스펙(I-V 커브, Overpotential)을 정밀하게 찍을 때 무조건 사용하는 셋업입니다. 에러(전압 강하)를 없애고 내가 만든 타겟 소자(DUT, Device Under Test) 하나의 성능만 완벽하게 분리해서 측정하기 위해 전극(센서)을 하나 더 추가한 것입니다.</p>
<p>구성:</p>
<ol>
<li><p>WE (Working Electrode, 작업 전극): 우리가 테스트할 대상입니다. (포스터에서는 몰리브덴을 코팅한 SS316 철망). DUT입니다.</p>
</li>
<li><p>CE (Counter Electrode, 상대 전극): 회로를 완성시켜 전류(Current)를 공급/흡수하는 셔틀 역할만 합니다. 보통 반응성이 없는 흑연(Carbon)이나 백금(Pt)을 씁니다.</p>
</li>
<li><p>RE (Reference Electrode, 기준 전극): 이게 핵심입니다. 전류는 0(Zero)으로 흐르지 않게 막아두고, 오직 전압(Voltage)의 기준점(Ground, 0V) 역할만 하는 고정밀 센서 핀입니다.</p>
</li>
</ol>
<p>CE와 WE 사이로는 팍팍 전기를 밀어 넣어서 반응(수소 발생)을 일으킵니다. 
(Power Loop)동시에 전압계는 RE와 WE 사이에 물려 있습니다. </p>
<p>RE로는 전류가 전혀 흐르지 않기 때문에 용액 저항에 의한 전압 강하($IR$ drop) 노이즈가 발생하지 않습니다.</p>
<p>결과적으로 오직 타겟 전극(WE) 표면에서 일어나는 순수한 화학 반응 전압(Overpotential)만 소수점 단위로 정밀하게 읽어낼 수 있습니다.</p>
<p>Force 루프 (CE - WE): 전류는 무조건 이 두 전극 사이로만 미친 듯이 흐릅니다. 물을 쪼개는 메인 전력(Power) 루프입니다.</p>
<p>Sense 루프 (RE - WE): 오직 타겟 소자(WE) 표면의 순수한 전압만 찍기 위해, 아주 가까운 곳에 핀(RE)을 하나 더 꽂아 전압계를 연결한 것입니다.</p>
<p>RE에 전류가 0인 이유: Op-Amp의 무한대 입력 임피던스
RE가 전압 강하($IR$ drop)를 어떻게 피하는지가 핵심입니다.</p>
<p>전위차계(Potentiostat) 내부 회로를 까보면, RE가 연결되는 단자는 매우 높은 입력 임피던스(보통 $10^{12} \Omega$ 이상)를 가진 버퍼용 Op-Amp에 물려 있습니다.</p>
<ul>
<li>오실로스코프의 프로브(10 M$\Omega$)를 회로에 찍었을 때, 프로브 쪽으로 전류가 거의 빨려 들어오지 않는 것과 완벽히 같은 원리입니다.</li>
<li>RE 전선 쪽으로는 전류($I$)가 $0$에 수렴하게 흐릅니다.</li>
<li>옴의 법칙 $V = I \times R$에서, 전류($I$)가 $0$이므로 용액 저항($R$)이 아무리 커도 전압 강하($V$)는 $0$이 됩니다. </li>
</ul>
<h3 id="4-종합-re의-진짜-역할">4. 종합: RE의 진짜 역할</h3>
<p>정리하자면 RE는 &quot;전류는 단 한 방울도 소모하지 않으면서, 오직 WE 표면의 전압 상태만 정밀하게 찍어서 읽어오는 하이 임피던스 스코프 프로브&quot;입니다.</p>
<ul>
<li>왜 굳이 은/염화은(Ag/AgCl) 같은 특수한 물질을 쓰느냐 하면, 측정 기준점(Ground, 0V) 자체가 이리저리 흔들리면 안 되기 때문입니다.</li>
<li>화학적으로 전압이 흔들리지 않고 딱 고정되어 있는 가장 안정적인 &#39;하드웨어 레퍼런스 칩(Reference Voltage IC)&#39; 역할을 하는 부품이 바로 Ag/AgCl 전극입니다.</li>
</ul>
<hr>
<h1 id="니켈-몰리브덴-인의-역할">니켈, 몰리브덴, 인의 역할</h1>
<h3 id="1-니켈-ni-물-분자-해체-담당-front-end-processor">1. 니켈 (Ni): &quot;물 분자 해체 담당 (Front-end Processor)&quot;</h3>
<p>AEM 환경(알칼리성)에서는 물(H2O)을 먼저 수소 원자(H)와 수산화 이온(OH-)으로 찢어야만 수소를 만들 수 있어.</p>
<p>몰리브덴 혼자서는 이 &#39;물을 찢는 작업(Water dissociation)&#39;을 정말 못해. 여기서 전력이 엄청나게 낭비돼.</p>
<p>반면 니켈은 물 분자를 붙잡아서 단번에 쪼개는 데 특화되어 있어. 즉, 니켈이 맨 앞에서 물을 빠르게 쪼개서 수소 원자를 뽑아주는 &#39;전처리(Front-end)&#39; 역할을 해주는 거야.</p>
<h3 id="2-몰리브덴-mo-수소-조립-및-배출-담당-back-end-processor">2. 몰리브덴 (Mo): &quot;수소 조립 및 배출 담당 (Back-end Processor)&quot;</h3>
<p>니켈이 물을 쪼개서 수소 원자들을 만들어주면, 이제 얘네들을 두 개씩 짝지어서 진짜 수소 가스(H2)로 조립한 뒤 날려 보내야 해.</p>
<p>니켈은 물은 잘 쪼개는데, 한 번 붙잡은 수소 원자를 꽉 쥐고 안 놔주려는 성질이 있어. 여기서 병목 현상(Bottleneck)이 발생해.</p>
<p>이때 몰리브덴이 니켈로부터 수소 원자를 쏙쏙 넘겨받아서, 수소 가스(H2)로 조립한 뒤 밖으로 부드럽게 방출해 줘.</p>
<p>화학에서는 이걸 이원 기능 메커니즘(Bifunctional Mechanism)이라고 부르는데, 회로 설계로 치면 앞단(Ni)과 뒷단(Mo)의 역할을 분담시켜 전체 데이터 처리 속도를 극대화한 파이프라인 설계야.</p>
<h3 id="3-인-p-전자-밀도-튜너-및-방어막-semiconductor-doping--passivation">3. 인 (P): &quot;전자 밀도 튜너 및 방어막 (Semiconductor Doping &amp; Passivation)&quot;</h3>
<p>가장 중요한 부분이야. 순수한 금속(Ni, Mo)만 섞어놓으면 표면이 쉽게 산화(녹슬음)되거나 효율이 생각보다 안 나와.</p>
<p>인(P)은 비금속 물질인데, 실리콘(Si) 웨이퍼에 불순물을 넣어 N형/P형 반도체를 만드는 <strong>도핑(Doping)</strong>과 똑같은 역할을 해.</p>
<p>인이 틈새로 들어가서 니켈과 몰리브덴의 &#39;전자 배치&#39;를 미세하게 뒤틀어버려. 그 결과 금속 내부의 전자들이 훨씬 더 스무스하게 이동하게 되어 전극 전체의 저항(Impedance)이 뚝 떨어져.</p>
<p>또한, 인이 겉면을 코팅하는 방어막 역할을 해서, 반응성이 강한 용액 속에서도 전극이 부식되지 않고 오래 버틸 수 있게 만들어 줘. (하드웨어 수명 증가)</p>
<p>결론적으로:
물 쪼개기 장인(Ni) + 수소 배출 장인(Mo) + 전자 흐름 최적화 및 방어구(P)를 합쳐서 <strong>&quot;전력은 가장 적게 먹으면서, 병목 현상 없이 수소를 뿜어내는 궁극의 하드웨어 소자&quot;</strong>를 만든 거야.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NiMoP electrodeposition for water splitting]]></title>
            <link>https://velog.io/@houston_guy2/NiMoP-electrodeposition-for-water-splitting</link>
            <guid>https://velog.io/@houston_guy2/NiMoP-electrodeposition-for-water-splitting</guid>
            <pubDate>Sat, 25 Apr 2026 02:34:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/houston_guy2/post/09e653d2-97cc-45c3-ac15-21802268299b/image.jpeg" alt=""></p>
<h1 id="공정순서">공정순서</h1>
<ol>
<li><p>초기화 (Base): 아무것도 묻지 않은 순수한 쌩 스테인리스 철망(ATSS)을 준비합니다.</p>
</li>
<li><p>용액 배합 (Mixing): 로봇 팔이 비커에 니켈(Ni), 몰리브덴(Mo), 인(P) 이온 용액을 특정 비율(예: 30:60:10)로 섞어 &#39;짬뽕 용액&#39;을 만듭니다.</p>
</li>
<li><p>전해증착 (Electrodeposition): 쌩 철망을 이 용액에 담그고 전류(-5 mA/cm²)를 인가합니다. 이때 Ni, Mo, P 세 가지 이온이 동시에 철망에 달라붙으면서 <strong>&#39;NiMoP 복합 촉매층&#39;</strong>이 단일 공정으로 완성됩니다.</p>
</li>
<li><p>성능 테스트 (Characterization): 코팅이 끝난 전극으로 수소 발생 전압(Overpotential)을 측정하여 엑셀에 데이터를 기록합니다.</p>
</li>
<li><p>루프 반복 (High-throughput): 로봇 팔이 기존 철망을 버리고, <strong>&#39;새로운 쌩 철망&#39;</strong>을 집어 듭니다. 그리고 Ni 40, Mo 50, P 10 비율로 섞인 <strong>&#39;새로운 비커&#39;</strong>에 담가서 3번~4번 과정을 반복합니다.</p>
</li>
</ol>
<h1 id="1-연구의-핵심-목표-abstract--introduction">1. 연구의 핵심 목표 (Abstract &amp; Introduction)</h1>
<ul>
<li><p>목적: 수전해(물 분해)를 통해 수소를 생산할 때, 값비싼 귀금속 대신 NiMoP (니켈-몰리브덴-인) 기반의 저렴한 촉매를 사용하는 것입니다.</p>
</li>
<li><p>문제점: 몰리브덴(Mo)을 전극에 코팅(전해증착, Electrodeposition)하는 것은 매우 어렵습니다. 코팅을 하는 동안 &#39;수소 발생 반응(HER)&#39;이 경쟁적으로 일어나면서 전극 표면의 국소적인 산성도(Local pH)가 높아지고, 이로 인해 금속 수산화물 침전물이 생겨 코팅을 방해하기 때문입니다.</p>
<pre><code>우리는 전원 장치에서 나온 전자가 오롯이 몰리브덴(Mo) 이온에 전달되어 코팅되길 원합니다.
하지만 전류를 흘리는 순간, 용액 안의 **&#39;물(H2O)&#39;이 먼저 전자를 가로채서 수소 가스(H2)로 날아가
버리는 반응(HER)**이 동시에 일어납니다.
</code></pre></li>
</ul>
<p>전자가 분산되니 코팅 효율이 뚝 떨어지고, 주변의 화학적 환경(pH)이 망가져서 코팅
이 제대로 안 되고 찌꺼기(수산화물)가 생겨버리는 것입니다.
그래서 이 연구실은 이런 방해 현상을 뚫고 
&#39;어떻게 전기적 조건(전류/전압)과 용액 농도를 세팅해야 완벽하게 코팅을 할 수 있을까?&#39;
를 찾아낸 것입니다.</p>
<pre><code>
- 해결책: 이 문제를 해결하기 위해 두 가지 강력한 무기를 사용했습니다.

   - In-situ Raman spectroscopy (실시간 라만 분광법): 반응이 일어나는 동안 표면의 화학적 변화와 pH 변화를 실시간으로 관찰.

   - High-throughput synthesis (고효율 자동화 합성): 수많은 합성 변수를 빠르게 테스트하여 최적의 조건을 찾아냄.

![](https://velog.velcdn.com/images/houston_guy2/post/3f8f9a29-bde1-4b6a-b141-1b47d8b3192f/image.png)

이 그림은 쌩 스테인리스 스틸 철망(SS316 mesh) 위에 우리가 원하는 몰리브덴 기반의 촉매(NiMoP)가 성공적으로 코팅된 최종 전극 소자의 모습을 형상화한 것입니다.

ATSS: 산 처리된 스테인리스 스틸(Acid-Treated Stainless Steel)의 약자로 보입니다. 코팅이 잘 붙도록 표면을 거칠게 깎아낸(Etching) 베이스 기판입니다.

![](https://velog.velcdn.com/images/houston_guy2/post/86f80a13-98ab-47e5-b6f9-ebf29d9382ea/image.png)

좌측 상단 (LSV 그래프 - Current density vs. Potential): * 전기화학에서 가장 기본이 되는 **I-V 커브(전압-전류 곡선)**입니다.

- X축은 걸어준 전압(Potential), Y축은 흐르는 전류(Current density)입니다.
- 그래프가 Y축으로 가파르게 치솟을수록 &quot;적은 전압으로도 엄청난 전류가 흐른다(수소가 많이 발생한다)&quot;는 뜻이므로 성능이 좋은 것입니다. 파란색 선(NiMoP-ATSS)이 가장 성능이 우수함을 보여줍니다.

우측 상단 (막대 그래프 - Overpotential):
- 특정 목표 전류(보통 10 mA/cm²)에 도달하기 위해 필요한 **과전압(Overpotential)**을 비교한 것입니다.
- 전자 소자의 **문턱 전압(Threshold Voltage)**과 비슷합니다. 이 막대가 낮을수록 전력 소모가 적은 고효율 촉매라는 뜻입니다.

좌측 하단 (Tafel plot - 타펠 도시):
- 전압과 전류에 로그(log)를 취해 기울기를 보는 그래프입니다.
- 이 기울기(Tafel slope)가 완만할수록 전압을 조금만 올려도 전류(반응 속도)가 기하급수적으로 뛴다는 것을 의미합니다. 소자의 스위칭 반응 속도나 효율성을 보여주는 지표입니다.

우측 하단 (Nyquist plot - 나이퀴스트 도시 / 임피던스):
- 교류(AC) 신호를 주파수별로 인가하여 측정한 임피던스(Impedance) 그래프입니다.
- 반원의 크기가 작을수록 전극 표면에서의 **전하 전달 저항(Charge Transfer Resistance)**이 작다는 뜻입니다. 즉, 전자가 회로에서 용액으로 넘어갈 때 저항 없이 스무스하게 넘어감을 증명하는 데이터입니다.

![](https://velog.velcdn.com/images/houston_guy2/post/2f7697e3-445c-46e5-9fcf-8230ac62ce3c/image.png)


이 사진은 앞서 설명한 **&quot;성공적인 코팅을 방해하는 에러 상황&quot;**의 실제 물리적 현상을 찍은 것입니다.
- 전극 표면에서 뽀글뽀글 올라오는 기포들이 바로 원치 않는 타이밍에 발생한 **수소 가스(HER)**입니다.
- 전자공학 관점에서는 이 기포들이 전극 표면에 달라붙어 물리적인 절연체(Insulator) 역할을 해버립니다. 저항이 급증하고 전류 흐름이 끊기면서 코팅이 엉망이 됩니다.
- 용액이 뿌옇게 흐려지는 것은 국소적 pH 상승으로 인해 금속 이온들이 코팅되지 못하고 바닥으로 떨어지는 **침전물(Precipitate)**이 생기고 있다는 증거입니다.


# 2. 실험 설계 및 실시간 분석 

## 기판 선택 (Section 2)
비싼 소재 대신 저렴하고 부식에 강한 **스테인리스 스틸 메쉬(SS316)**를 기판으로 사용했습니다.

### Electrolyzer 3D 설계도
![](https://velog.velcdn.com/images/houston_guy2/post/51b68538-efcc-4e0c-8f9e-ef62530e08d8/image.png)

실제로 제작한 전해조(Electrolyzer) 하드웨어 패키징 도면입니다.

가장 가운데에 촉매가 코팅된 철망(전극)과 AEM 분리막이 들어갑니다. 그 양옆으로는 물(전해질)을 고르게 분배하고 발생한 가스(수소/산소)를 원활하게 빼내기 위한 유로(Flow field, 미세한 홈이 파인 판)가 겹겹이 쌓입니다.

양쪽 끝에는 두꺼운 엔드 플레이트(End plate)를 대고 볼트로 강하게 압착하여 조입니다. 내부 압력이 높아져도 용액이나 가스가 밖으로 새지 않도록 물리적인 밀봉(Sealing)을 하는 패키징 공정입니다.

## 실시간 라만 분석 (Section 3):
![](https://velog.velcdn.com/images/houston_guy2/post/8f000686-d322-47ba-a25b-a85798c5cc91/image.png)

전압 변화에 따른 표면 스펙트럼 (Effect of different bias)
- 내용: 전극에 걸어주는 인가 전압(Bias potential)을 -0.1 V에서 -1 V까지 점진적으로 내리면서(전기화학에서는 음의 방향으로 전압을 가할수록 반응이 강해집니다), 표면 상태가 어떻게 변하는지 광학 센서로 스캔한 것입니다.

- 우측 상단 그래프: X축은 라만 편이(Raman shift, 빛의 주파수 변화량), Y축은 신호의 세기(Intensity)입니다. 가로선들이 층층이 쌓여 있는데, 각각 다른 전압을 걸었을 때의 주파수 응답(Frequency Response)을 의미합니다. 전압이 음의 방향으로 커질수록 특정 주파수 대역에서 산봉우리(Peak)들이 새롭게 튀어나오거나 모양이 변하는 것을 볼 수 있습니다.

- 해석: 전압이 변함에 따라 표면에 붙어있는 이온들의 물리/화학적 결합 상태가 실시간으로 달라지고 있음을 데이터로 증명한 것입니다.

 전압(Bias)을 변화시키면서 표면 화학 상태가 어떻게 변하는지 실시간으로 측정했습니다.

 ![](https://velog.velcdn.com/images/houston_guy2/post/70a1bd95-d69a-48b9-af25-b0383beb8036/image.png)


인산염 완충액(Phosphate buffer)을 마커로 사용하여, 수소 발생(HER)이 일어날 때 전극 표면의 국소적 pH가 어떻게 급증하는지 광학적으로 증명해냈습니다. 즉, &quot;왜 코팅이 잘 안되는가&quot;에 대한 원인을 시각적 데이터로 밝혀낸 것입니다.

# 3. 최적화 및 고효율 스크리닝 
## 전해조 최적화 (Section 4)

선 분석 결과를 바탕으로, 에러(수소 발생 및 찌꺼기 생성)를 피해서 **&#39;성공적으로 코팅(Electrodeposition)을 완료하기 위한 최적의 공정 레시피와 그 결과물&#39;**을 보여주는 파트입니다.

![](https://velog.velcdn.com/images/houston_guy2/post/37948ba6-133b-4e8f-99cc-c971e44cc7c7/image.png)

### 공정 파라미터 최적화 (Optimization Conditions)
이 연구실이 찾아낸 &#39;코팅 실패를 막는 4가지 핵심 세팅값&#39;입니다.

- 산세척 (Acid-etching): 쌩 철망(SS)을 산성 용액으로 씻어냅니다. 이는 단순히 때를 벗기는 것이 아니라, 표면을 미세하게 깎아내어(Etching) 금속 이온들이 잘 달라붙을 수 있는 &#39;앵커(Anchor)&#39; 자리를 많이 만들어주는 전처리 공정입니다.

- 대기 시간 (Waiting time): 전기를 가하기 전에, 이온들이 철망 표면에 골고루 자리 잡고 안정화될 때까지 기다려주는 시간(Settling time)입니다.

- 적정 전류 밀도 (Suitable current density): -5 mA/cm². 전압이나 전류를 무작정 높게 때려 넣으면 앞서 말한 수소 가스(HER)가 폭발적으로 발생해 코팅이 망가집니다. 에러가 나지 않으면서 코팅은 잘 되는 &#39;스위트 스팟(Sweet spot)&#39; 전류값을 찾아낸 것입니다.

- 고농도 첨가제 사용 (High concentration of additives): 용액에 LiCl, NaCl, NH4Cl 같은 염화물(소금기)을 잔뜩 집어넣습니다. 이는 용액 전체의 전기 전도도(Conductivity)를 확 끌어올려 저항을 줄이고, 동시에 수소 발생 반응(HER)을 억제하는 핵심 패치(Patch) 역할을 합니다.

### 하드웨어 제조 공정 모식도
- SSM -&gt; ATSS -&gt; Electro-deposition -&gt; NiMoP-ATSS

- 빈 기판 입고(SSM) -&gt; 화학적 표면 처리(ATSS) -&gt; 전극 연결 후 도금 진행 -&gt; 최종 촉매 전극 완성(NiMoP-ATSS)이라는 전체 소자 제작 파이프라인을 보여줍니다.

- 가운데 비커 그림을 보면 (+)극에는 카본(CE: Carbon), (-)극에는 철망(WE: Working Electrode)을 연결하고 전압을 인가하는 전형적인 전해증착 회로 셋업을 확인할 수 있습니다.

### 완성 소자의 품질 검사 (Material Characterization)
![](https://velog.velcdn.com/images/houston_guy2/post/4c7bf712-d6e2-487f-8744-2a83e8e98337/image.png)

하단에 있는 복잡한 그래프와 표, 사진들은 코팅이 끝난 전극이 우리가 의도한 대로 잘 만들어졌는지 검증(Verification)하는 교차 검증 데이터들입니다. 반도체 공정 후의 소자 불량 검사와 완벽히 같습니다.

XRD &amp; XPS (좌측 그래프들): X선을 쏴서 내부 결정 구조와 화학적 결합 상태를 보는 장비입니다. 코팅된 물질이 불순물 없이 순수한 &#39;NiMoP&#39; 합금이 맞는지 성분을 검사하는 성적서입니다.

표 (Table): 조건에 따라 Ni(니켈), Mo(몰리브덴), P(인)의 비율이 몇 %로 코팅되었는지 정량적으로 보여줍니다.

SEM/EDS 매핑 (우측 하단 현미경 사진):

SEM: 전자현미경 사진입니다. 철망 표면에 촉매가 매끄럽게 잘 발렸는지 육안으로 확인하는 물리적 표면 검사입니다.

EDS: 특정 원소가 표면에 어떻게 분포하는지 색깔로 보여줍니다. Ni, Mo, P가 특정 구역에 뭉치지 않고 균일하게(Uniform) 깔려 있음을 시각적으로 증명합니다.

## 고효율 합성 및 스크리닝 (Section 5)
![](https://velog.velcdn.com/images/houston_guy2/post/8792974d-f65b-4738-a2c2-da4b580c5c04/image.png)

High-throughput HER overpotential results (데이터 시각화 및 최적화)
우측에 있는 무지개색 삼각형 그래프는 이 자동화 시스템이 밤낮없이 수백 개의 전극을 찍어내고 테스트하여 얻어낸 최종 결과물, **&#39;삼원 상태도(Ternary contour plot)&#39;**입니다.

그래프 해석: 삼각형의 세 꼭짓점은 각각 Ni(니켈), Mo(몰리브덴), P(인)의 함량 100%를 의미합니다. 내부의 한 점을 찍으면 세 물질의 특정 혼합 비율이 됩니다.

색깔의 의미 (Z축): 색상이 붉은색일수록 수소 발생을 위한 과전압(Overpotential)이 높아 효율이 나쁜 똥망 세팅입니다. 반면 파란색(Deep blue)으로 파인 부분이 전력 소모가 가장 적은 **최적의 &#39;황금 비율(Sweet spot)&#39;**입니다.

결론: &quot;우리가 만든 자동화 시스템으로 전 영역을 스캔(Parameter sweep)해본 결과, 저 파란색 지점의 조성비로 촉매를 섞었을 때 수소가 가장 싸고 효율적으로 잘 나온다&quot;는 것을 데이터로 완벽하게 증명한 것입니다.
- 자동화된 실험실(Automatic experimenting lab) 세팅을 통해 다양한 비율의 촉매를 빠르게 합성하고 테스트했습니다.
- 우측 하단의 삼원 상태도(Ternary plot)는 수많은 조성 비율 중 어느 지점에서 수소 발생 효율(Overpotential)이 가장 좋은지를 한눈에 보여주는 핵심 결과물입니다.


</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Tournament Predictors, Branch-Target Buffers (BTB)]]></title>
            <link>https://velog.io/@houston_guy2/Tournament-Predictors-Branch-Target-Buffers-BTB</link>
            <guid>https://velog.io/@houston_guy2/Tournament-Predictors-Branch-Target-Buffers-BTB</guid>
            <pubDate>Thu, 23 Apr 2026 19:06:11 GMT</pubDate>
            <description><![CDATA[<p><em>Tournament Predictor (토너먼트 예측기)*</em>는 말 그대로 칩 내부에서 두 개의 서로 다른 예측기를 경쟁시켜서, <strong>&quot;지금 이 분기문에서는 누구 말을 듣는 게 더 정확할까?&quot;</strong>를 하드웨어가 스스로 판단하고 선택하는 <strong>메타 예측기(Meta-Predictor)</strong>입니다.</p>
<p>왜 이런 복잡한 방식을 사용하는지, 하드웨어 구조와 논리를 쪼개어 설명해 드리겠습니다.</p>
<h2 id="1-도입-배경-완벽한-단일-예측기는-없다">1. 도입 배경: 완벽한 단일 예측기는 없다</h2>
<p>로컬 예측기(Local Predictor): 오직 자기 자신의 과거만 봅니다. 아주 단순하고 규칙적으로 뱅글뱅글 도는 for 루프문(Loop)의 횟수를 예측하는 데는 귀신같이 정확합니다. 하지만 앞서 본 널뛰기 패턴이나 다른 변수의 영향을 받는 조건문에서는 박살이 납니다.</p>
<p>글로벌/상관 예측기(Global/Correlated Predictor): 주변의 문맥(다른 분기문의 결과)을 봅니다. 복잡한 if-else 구조나 얽혀있는 조건문을 예측하는 데 강력합니다. 하지만, 완전히 독립적이고 단순한 루프문을 돌 때는 오히려 쓸데없는 남의 문맥을 참조하느라 혼란에 빠져 정확도가 떨어질 때가 있습니다.</p>
<p>결론: 실제 프로그램 안에는 로컬이 잘 맞추는 단순한 분기문과 글로벌이 잘 맞추는 복잡한 분기문이 섞여 있습니다. 따라서 두 개를 다 만들어 놓고, 상황에 맞게 더 잘 맞추는 예측기를 고르자는 것이 토너먼트 예측기의 철학입니다.</p>
<h2 id="2-하드웨어-구조-3개의-컴포넌트">2. 하드웨어 구조: 3개의 컴포넌트</h2>
<p>토너먼트 예측기는 내부에 3개의 하드웨어 테이블을 독립적으로 굴립니다.</p>
<ul>
<li>Local Predictor Array: 자신의 과거 기록(PC 주소)만 참조하여 0 또는 1을 내뱉는 예측기.</li>
<li>Global Predictor Array: 글로벌 히스토리(GHR)를 참조하여 0 또는 1을 내뱉는 예측기.</li>
<li>Selector (선택기 / 메타 예측기): 이 둘 중 누구의 예측값을 최종적으로 채택할지 결정하는 &#39;심판&#39; 역할을 합니다.</li>
</ul>
<h2 id="3-selector의-동작-원리-the-referee">3. Selector의 동작 원리 (The Referee)</h2>
<p>Selector 역시 우리가 앞서 배운 <strong>2비트 포화 카운터(2-bit Saturating Counter)</strong>로 동작합니다. 다만, 뛰느냐(Taken) 안 뛰느냐(Not Taken)를 기록하는 것이 아니라, <strong>&quot;누가 정답을 맞혔는가&quot;</strong>를 기록합니다.</p>
<p>상태값 (예시):</p>
<ul>
<li>00, 01: &quot;최근에 로컬(Local) 예측기가 계속 정답을 맞히더라. 이번에도 로컬의 예측값을 파이프라인에 전달해야지.&quot;</li>
<li>10, 11: &quot;최근에 글로벌(Global) 예측기가 계속 정답을 맞히더라. 이번엔 글로벌의 예측값을 파이프라인에 전달해야지.&quot;</li>
</ul>
<p>업데이트 규칙:</p>
<ul>
<li>두 예측기가 모두 맞히거나 모두 틀리면 카운터는 변하지 않습니다.</li>
<li>로컬은 틀렸는데 글로벌이 맞히면, 카운터를 글로벌 쪽으로 1 증가(+1)시킵니다.</li>
<li>글로벌은 틀렸는데 로컬이 맞히면, 카운터를 로컬 쪽으로 1 감소(-1)시킵니다.</li>
</ul>
<p>이 슬라이드는 지금까지 배운 화려한 분기 예측 기술(로컬, 글로벌, 토너먼트)들이 고전적인 5단계 MIPS 파이프라인의 물리적 구조 앞에서는 무용지물이 되는 치명적인 모순을 뼈아프게 지적하고 있습니다.</p>
<p>아무리 예측기가 똑똑하게 &quot;뛴다(Taken)&quot;라고 정답을 맞춰도 파이프라인 지연 시간이 전혀 단축되지 않는 이유를 설계 관점에서 쪼개어 분석해 드리겠습니다.</p>
<h1 id="implementing-branch-histories">Implementing Branch Histories</h1>
<h2 id="1-if-fetch-단계의-맹점-명령어의-정체를-모른다">1. IF (Fetch) 단계의 맹점: &quot;명령어의 정체를 모른다&quot;</h2>
<p>프로세서의 첫 단계인 <strong>IF(Instruction Fetch)</strong>에서는 단순히 메모리에서 32비트짜리 기계어 덩어리를 가져올 뿐입니다.</p>
<p>예측기가 &quot;이번엔 뛴다!&quot;라고 강력하게 주장해도, 정작 방금 가져온 32비트 명령어가 덧셈(ADD)인지, 분기문(BEQ)인지 자체를 이 시점에서는 알 길이 없습니다. 해독(Decoding)을 거쳐야만 정체를 알 수 있습니다.</p>
<h2 id="2-목적지-주소target-address의-부재">2. 목적지 주소(Target Address)의 부재</h2>
<p>백번 양보해서 방금 가져온 것이 분기문이라는 것을 IF 단계에서 기적적으로 알았고, 예측기가 점프할 것(Taken)이라는 것도 맞췄다고 가정해 봅시다.</p>
<p>하지만 <strong>&quot;어디로(Where) 뛸 것인가?&quot;</strong>를 모릅니다. 목적지 주소를 알려면 명령어의 하위 비트(Offset)를 뜯어서 현재 PC 주소와 더하는 산술 연산 로직을 거쳐야만 합니다. 어디로 갈지 모르면 다음 명령어를 미리 당겨올(Fetch) 수 없습니다.</p>
<h2 id="3-id-decode-단계의-역설-알았을-때는-이미-늦었다">3. ID (Decode) 단계의 역설: &quot;알았을 때는 이미 늦었다&quot;</h2>
<p>MIPS 파이프라인에서는 ID(Instruction Decode) 단계에 도달해야 비로소 이 명령어가 분기문임을 해독하고, 목적지 주소를 계산해 냅니다.</p>
<p>치명적 문제 (Haven&#39;t saved any time!): 방향 예측을 미리 하든 안 하든, 어차피 목적지 주소를 알기 위해 ID 단계까지 멍하니 기다려야 한다면 예측을 미리 해둔 의미가 전혀 없습니다. 다음 명령어를 미리 Fetch 하려고 예측하는 것인데 주소를 몰라서 기다린다면, 결국 예측을 안 했을 때와 똑같이 1사이클 패널티(Control Stall)를 꼼짝없이 맞게 됩니다.</p>
<h1 id="branch-target-buffers-btb">Branch-Target Buffers (BTB)</h1>
<h3 id="1-일반적인-mips-파이프라인의-현실-extra-id-cycle">1. 일반적인 MIPS 파이프라인의 현실 (Extra ID cycle)</h3>
<p>아무런 보조 장치가 없는 일반적인 파이프라인에서는 명령어를 처음 가져오는 IF(Fetch) 단계에서 다음 주소(Next PC)를 바로 알아낼 방법이 없습니다. 최소한 다음 단계인 ID(Decode) 단계까지 한 사이클을 온전히 기다려야만 비로소 다음 3가지를 알아낼 수 있습니다.</p>
<ul>
<li>방금 메모리에서 긁어온 32비트 덩어리가 하필 &#39;분기 명령어&#39;라는 사실</li>
<li>조건이 충족되어 진짜로 &#39;점프(Taken)&#39; 해야 한다는 사실</li>
<li>현재 PC에 Offset을 더하는 산술 연산을 통해 알아낸 실제 &#39;목적지 주소(Target Address)&#39;</li>
</ul>
<h3 id="2-방향-예측의-반쪽짜리-진실-branch-prediction-alone-doesnt-help">2. 방향 예측의 반쪽짜리 진실 (Branch prediction alone doesn&#39;t help)</h3>
<p>우리가 지금까지 다루었던 1비트/2비트 카운터, 상관 예측기, 토너먼트 예측기 등은 오직 <strong>&quot;뛸 것인가, 말 것인가?(Taken / Not Taken)&quot;</strong>만 예측해 줍니다.
예측기가 &quot;이번엔 100% 뜁니다!&quot;라고 확신하더라도, 정작 &quot;그래서 어디로(Next PC) 뛸 것인데?&quot;라는 질문에 대답하지 못하면 무용지물입니다. 목적지 주소 계산이 끝날 때까지 다음 명령어를 Fetch 할 수 없으므로 파이프라인은 멈춰서 기다려야 합니다.</p>
<h3 id="3-완전한-해결책-btb-branch-target-buffers">3. 완전한 해결책: BTB (Branch-Target Buffers)</h3>
<p>이 문제를 근본적으로 해결하기 위해 칩 내부에 추가한 하드웨어가 바로 BTB입니다.
BTB는 과거에 분기문이 &#39;어디로 뛰었는지&#39; 그 목적지 주소(Target Address) 자체를 통째로 캐싱해 두는 전용 메모리입니다.</p>
<p>동작 원리: IF 단계에서 현재 명령어를 가져오기 위해 PC 주소를 메모리에 던질 때, 이 PC 주소를 BTB 캐시에도 동시에 던집니다.</p>
<p>효과: 만약 BTB에 &quot;이 주소는 예전에 점프했던 분기문이고, 그때 목적지는 0x4000번지였어&quot;라는 기록(Hit)이 남아있다면, ID 단계까지 가서 명령어를 해독하고 주소 계산을 할 필요가 없습니다. 다음 사이클에 지체 없이 곧바로 0x4000번지에서 명령어를 당겨옵니다.</p>
<p>결론적으로 방향 예측기(BHT)의 &quot;뛴다&quot;는 판단과, 주소 예측기(BTB)의 &quot;이곳으로 간다&quot;는 정보가 한 쌍으로 맞물려 돌아가야만 파이프라인의 지연(Stall)을 완벽하게 지워낼 수 있습니다.</p>
<h1 id="btb-schematic">BTB Schematic</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1827c2aa-21c1-4c48-9a11-372053640850/image.png" alt=""></p>
<h3 id="1-주소-입력-및-검색-look-up">1. 주소 입력 및 검색 (Look up)</h3>
<ul>
<li><p>PC of instruction to fetch: 파이프라인이 IF 단계에서 메인 메모리로부터 명령어를 가져오기 위해 사용하는 현재 PC 주소입니다.</p>
</li>
<li><p>하드웨어는 메인 메모리에 주소를 요청함과 동시에, 이 PC 주소를 BTB 테이블의 왼쪽 구역(Tag 배열)으로 보냅니다 (Look up).</p>
</li>
</ul>
<h3 id="2-하드웨어-비교기-comparator-">2. 하드웨어 비교기 (Comparator &#39;=&#39;)</h3>
<p>다이어그램 하단에 있는 동그란 = 기호가 이 아키텍처의 핵심 논리 회로입니다. 현재 PC 주소와 BTB 안에 과거 기록으로 저장된 주소들을 병렬로 비교합니다.</p>
<ul>
<li><p>No (Miss 발생): 일치하는 주소가 없는 경우입니다. 하드웨어는 &quot;이 명령어는 분기문이 아니거나(예: 일반 덧셈 명령어), 태어나서 처음 보는 분기문이구나&quot;라고 판단합니다. 따라서 특별한 조치 없이 PC + 4를 하여 다음 명령어를 순차적으로 가져옵니다 (proceed normally).</p>
</li>
<li><p>Yes (Hit 발생): 일치하는 주소가 있는 경우입니다. 명령어 해독(ID 단계)을 거치지도 않았는데, 단지 메모리 주소만 보고도 <strong>&quot;어! 이거 예전에 점프했던 그 분기문이네!&quot;</strong>라고 즉시 명령어의 정체를 파악해 낸 것입니다.</p>
</li>
</ul>
<h3 id="3-예측-확인-및-목적지-반환-predicted-pc">3. 예측 확인 및 목적지 반환 (Predicted PC)</h3>
<p>Hit가 발생했다고 해서 무조건 목적지로 점프하는 것은 아닙니다. 테이블의 오른쪽 구역을 확인해야 합니다.</p>
<ul>
<li><p>Branch predicted taken or untaken (가장 오른쪽 칸): 우리가 앞서 지겹게 다루었던 1비트, 2비트, 혹은 토너먼트 방향 예측기의 상태값이 들어있는 곳입니다. 여기서 &quot;이번에도 뛴다(Taken)&quot;라고 결론이 나면 다음 단계를 수행합니다.</p>
</li>
<li><p>Predicted PC (가운데 넓은 칸): &quot;어디로 뛸 것인가&quot;에 대한 정답입니다. 과거에 계산해 두었던 목적지 주소를 여기서 즉시 끄집어냅니다.</p>
</li>
<li><p>결과 적용: 하드웨어는 이 Predicted PC 값을 파이프라인의 다음 Fetch 주소로 강제로 덮어씌웁니다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/3be17858-d9a8-45e2-846f-ccef9890d8b0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ Correlated Prediction/ final --]]></title>
            <link>https://velog.io/@houston_guy2/Correlated-Prediction-final-</link>
            <guid>https://velog.io/@houston_guy2/Correlated-Prediction-final-</guid>
            <pubDate>Thu, 23 Apr 2026 18:34:31 GMT</pubDate>
            <description><![CDATA[<h1 id="1-global-prediction-correlated-prediction">1. Global Prediction: Correlated Prediction</h1>
<pre><code class="language-c">Code fragment from eqntott:
if (aa==2) aa=0; (b1)
if (bb==2) bb=0; (b2)
if (aa!=bb) { … (b3)

MIPS code (aa=R1, bb=R2):

    SUBUI R3,R1,#2 ;(aa-2)
    BNEZ R3,L1 ; branch b1 (aa!=2)
    ADD R1,R0,R0 ; aa=0
L1: SUBUI R3,R2,#2 ;(bb-2)
    BNEZ R3,L2 ; branch b2 (bb!=2)
    ADD R2,R0,R0 ; bb=0
L2: SUBU R3,R1,R2 ;(aa-bb)
    BEQZ R3,L3 … ; branch b3 (aa==bb)

b3 is taken if both b1 and b2 are not taken</code></pre>
<p>핵심은 <strong>&quot;서로 관련이 없어 보이는 앞선 두 개의 if문이, 세 번째 if문의 결과를 100% 결정지어 버린다&quot;</strong>는 것을 증명하는 것입니다. 이것이 바로 주변 문맥을 파악하는 <strong>상관 예측(Correlated Prediction)</strong>의 존재 이유입니다.</p>
<blockquote>
<p>BNEZ (Branch if Not Equal to Zero)
뜻: 검사하는 레지스터의 값이 0이 아니면(Not Equal to 0) 지정된 라벨로 점프해라.</p>
</blockquote>
<blockquote>
<p>BEQZ (Branch if Equal to Zero)
뜻: 검사하는 레지스터의 값이 정확히 0과 같으면(Equal to 0) 지정된 라벨로 점프해라.</p>
</blockquote>
<h2 id="1-c-코드의-숨겨진-함정-상관관계">1. C 코드의 숨겨진 함정 (상관관계)</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/56a94f77-1124-4b6d-bba0-e9629c7a0bb1/image.png" alt=""></p>
<p>먼저 C 코드를 논리적으로 따라가 보겠습니다.</p>
<p>b1: 만약 aa == 2라면, aa를 0으로 만듭니다.
b2: 만약 bb == 2라면, bb를 0으로 만듭니다.
b3: 만약 aa != bb라면, 괄호 안으로 들어갑니다.</p>
<p>여기서 아주 특정한 상황을 가정해 봅시다. 만약 애초에 aa도 2였고, bb도 2였다면 어떻게 될까요?</p>
<p>b1을 거치면서 aa는 0이 됩니다.
b2를 거치면서 bb도 0이 됩니다.
이제 b3 조건문에 도달했습니다. aa는 0이고 bb도 0입니다. 둘은 무조건 같습니다.
따라서 aa != bb라는 조건은 <strong>무조건 거짓(False)</strong>이 됩니다.</p>
<h2 id="2-mips-어셈블리로-보는-하드웨어의-동작">2. MIPS 어셈블리로 보는 하드웨어의 동작</h2>
<p>이제 이 상황을 슬라이드에 있는 하드웨어 분기(Branch) 명령어의 시선으로 다시 보겠습니다. (참고로 분기 조건이 참이어서 다른 곳으로 뛰면 Taken, 조건이 거짓이어서 바로 다음 줄을 실행하면 Not Taken입니다.)</p>
<ul>
<li><p>b1 (BNEZ R3, L1): aa != 2일 때 L1으로 점프(Taken)합니다. 즉, aa == 2여서 값을 0으로 바꾸는 작업을 하려면 점프를 하지 말아야 합니다 (Not Taken).</p>
</li>
<li><p>b2 (BNEZ R3, L2): bb != 2일 때 L2로 점프(Taken)합니다. 마찬가지로 bb == 2여서 값을 0으로 바꾸려면 점프를 하지 말아야 합니다 (Not Taken).</p>
</li>
<li><p>b3 (BEQZ R3, L3): aa == bb일 때 L3로 점프(Taken)하여 if 블록을 건너뜁니다.</p>
</li>
</ul>
<h2 id="3-결론-b3-is-taken-if-both-b1-and-b2-are-not-taken">3. 결론: &quot;b3 is taken if both b1 and b2 are not taken&quot;</h2>
<p>위의 1번과 2번 분석을 합치면 슬라이드 맨 마지막의 마법 같은 문장이 완성됩니다.</p>
<p>앞선 b1과 b2가 둘 다 점프하지 않았다면(Not Taken), 그것은 aa와 bb가 모두 2였다는 뜻이고, 둘 다 0으로 바뀌었으므로 무조건 aa == bb가 성립합니다. 따라서 b3에서는 계산해 볼 것도 없이 무조건 L3로 건너뛰어야(Taken) 합니다.</p>
<ul>
<li><p>기존 Local Predictor의 멍청함: 오직 b3 자신의 과거 기록만 봅니다. b1, b2에서 무슨 일이 있었는지 모르기 때문에, b3에서 점프를 할지 말지 찍다가 계속 틀립니다.</p>
</li>
<li><p>Global (Correlated) Predictor의 영리함: 하드웨어가 직전에 실행된 b1과 b2의 결과를 기억해 둡니다. &quot;어? 방금 b1 안 뛰었고(0), b2도 안 뛰었네(0)? 그럼 b3는 100% 무조건 뛰는(1) 자리구나!&quot; 하고 완벽하게 정답을 예측해 냅니다.</p>
</li>
</ul>
<p>즉, 이 슬라이드는 단일 분기문 하나만 쳐다보는 시야에서 벗어나, <strong>&quot;프로그램 전체의 실행 맥락(Global History)을 읽어야만 예측 실패율의 한계를 돌파할 수 있다&quot;</strong>는 것을 완벽하게 증명하는 예시입니다.</p>
<h1 id="2-even-simpler-example">2. Even Simpler Example</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/aac164f7-449c-4c35-86b6-9dd99d62773c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/cecfcbd4-f0c5-422e-b45b-387cc7fc9f84/image.png" alt=""></p>
<p>b2 will not be taken if b1 is not taken</p>
<ul>
<li>Correlated Predictor의 해결책: 하드웨어가 직전 분기문 b1이 어땠는지를 기억합니다. 만약 방금 전 b1이 <strong>Not Taken(0)</strong>이었다면, 하드웨어는 과거의 기록이나 패턴을 복잡하게 계산할 필요도 없이 <strong>&quot;방금 b1이 0이었어? 그럼 이번 b2도 100% 0(Not Taken)이겠네!&quot;</strong>라고 완벽하게 적중시켜 버립니다.</li>
</ul>
<h1 id="3-using-1-bit-predictor">3. Using 1-bit Predictor</h1>
<ul>
<li>Suppose initial value of ‘d’ alternates between 2 and 0. The initial value of b1 and b2 predictor is “NT”</li>
<li>All branches are mispredicted!!</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a2324ff4-9468-43a5-8fdc-4a6b9d8dbca8/image.png" alt=""></p>
<p>C code:
if (d==0) d=1; if (d==1) </p>
<p>이 슬라이드는 방금 전 보셨던 코드를 <strong>기존의 1비트 로컬 예측기(1-bit Local Predictor)</strong>로 돌렸을 때 발생하는 <strong>대참사(0% 적중률)</strong>를 증명하는 표입니다.</p>
<h2 id="3-결론-로컬-예측기local-predictor의-치명적-한계">3. 결론: 로컬 예측기(Local Predictor)의 치명적 한계</h2>
<p>이 표가 말하고자 하는 핵심은 명확합니다. 1비트 로컬 예측기는 널뛰기(Alternating) 패턴에 완벽하게 무방비라는 것입니다.</p>
<p>예측기가 자신의 &#39;과거&#39;만 보고 미래를 찍기 때문에, 정답이 T -&gt; NT -&gt; T -&gt; NT로 바뀌는 상황에서는 항상 정답의 정확히 반대만 찍으면서 영원히 한 박자 늦은 뒷북을 치게 됩니다. 그 결과 예측 실패율 100%라는 재앙이 발생합니다.</p>
<h1 id="4-correlating-predictors">4. Correlating Predictors</h1>
<ul>
<li>Have different predictions for the current branch depending on the previously executed branch instruction was taken or not.</li>
</ul>
<p>앞서 로컬 예측기가 2 -&gt; 0 -&gt; 2 -&gt; 0 패턴에서 100% 오답을 내며 처참하게 박살 나는 것을 보셨습니다.
이 슬라이드는 바로 그 동일한 악조건에서, <strong>상관 예측기(Correlated Predictors)</strong>가 어떻게 단 한 번의 초기 학습만으로 나머지 모든 예측을 100% 적중시켜 버리는지 그 마법 같은 원리를 증명합니다.</p>
<h2 id="1-표기법의-비밀-기억의-방을-2개로-쪼개다">1. 표기법의 비밀: &quot;기억의 방을 2개로 쪼개다&quot;</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b1f37adc-c502-4e7f-8fce-723a6d4269b6/image.png" alt=""></p>
<p>이 예측기는 앞선 로컬 예측기처럼 무식하게 과거 하나만 기억하지 않습니다. <strong>&quot;내 직전 분기문이 뛰었는가, 안 뛰었는가&quot;</strong>에 따라 참조하는 기억의 방이 다릅니다.</p>
<p>왼쪽 / 오른쪽 표기법의 의미:</p>
<ul>
<li>왼쪽 자리: &quot;내 직전 분기문이 안 뛰었을 때(NT)&quot; 꺼내 볼 예측값.</li>
<li>오른쪽 자리: &quot;내 직전 분기문이 뛰었을 때(T)&quot; 꺼내 볼 예측값.</li>
</ul>
<p>굵은 글씨 (Bold): 하드웨어가 직전 상황을 보고 실제로 선택한 예측값입니다.</p>
<h2 id="2-타임라인-분석-패턴을-분리해-내는-과정">2. 타임라인 분석: 패턴을 분리해 내는 과정</h2>
<pre><code>b1과 b2는 코드상에서 서로 다른 줄에 위치한, 메모리 주소(PC)가 완전히 다른 명령어입니다.

하드웨어 예측기 내부에는 수천 개의 방(Entry)이 있고, PC 주소를 인덱스로 삼아 찾아갑니다. 따라서 b1 전용 장부와 b2 전용 장부는 아예 다른 칸에 따로 존재합니다.

초기 상태에서 b1의 장부도 NT/NT, b2의 장부도 NT/NT로 각각 세팅되어 시작합니다.</code></pre><p><img src="https://velog.velcdn.com/images/houston_guy2/post/f7adbf8c-5977-4991-ae31-05417863d864/image.png" alt="">
표를 위에서부터 한 줄씩 따라가며, 하드웨어가 어떻게 패턴을 파악하는지 보겠습니다. (최초 시작 전 과거 기록은 NT였다고 가정합니다.)</p>
<p>첫 번째 줄 (d=2): 예열 및 학습 (유일하게 틀리는 구간)</p>
<ul>
<li><p>b1: 직전이 NT였으므로 왼쪽 방을 엽니다. NT/NT에서 왼쪽인 NT를 예측합니다. 하지만 실제는 T입니다. (예측 실패). 방금 열어본 왼쪽 방을 T로 고쳐 씁니다. ➔ New: T/NT</p>
</li>
<li><p>b2: 자, 방금 전 b1이 T였습니다! 그래서 b2는 오른쪽 방을 엽니다. NT/NT에서 오른쪽인 NT를 예측합니다. 하지만 실제는 T입니다. (예측 실패). 방금 열어본 오른쪽 방을 T로 고쳐 씁니다. ➔ New: NT/T</p>
</li>
</ul>
<p>두 번째 줄 (d=0): 마법의 시작</p>
<ul>
<li><p>b1: 방금 전(첫 번째 줄의 b2)이 T였습니다. 그래서 b1은 오른쪽 방을 엽니다. 아까 고쳐진 T/NT에서 오른쪽인 NT를 예측합니다. 실제 값도 NT입니다. (예측 적중!) 방 크기(값)는 변함없이 T/NT 유지.</p>
</li>
<li><p>b2: 방금 전 b1이 NT였습니다. b2는 왼쪽 방을 엽니다. NT/T에서 왼쪽인 NT를 예측합니다. 실제 값도 NT입니다. (예측 적중!)</p>
</li>
</ul>
<p>세 번째 줄 (d=2): 완벽한 적응</p>
<ul>
<li><p>b1: 방금 전(두 번째 줄의 b2)이 NT였습니다. b1은 왼쪽 방을 엽니다. T/NT에서 왼쪽인 T를 예측합니다. 실제 값도 T입니다. (예측 적중!)</p>
</li>
<li><p>b2: 방금 전 b1이 T였습니다. b2는 오른쪽 방을 엽니다. NT/T에서 오른쪽인 T를 예측합니다. 실제 값도 T입니다. (예측 적중!)</p>
</li>
</ul>
<p>Only misprediction on the first iteration when d=2</p>
<hr>
<h1 id="헷갈리는-부분">헷갈리는 부분</h1>
<h3 id="완전히-별개인-부품-예측-카운터-테이블-sram">완전히 별개인 부품: 예측 카운터 테이블 (SRAM)</h3>
<ul>
<li><p>제가 앞서 &quot;별개&quot;라고 말씀드린 부분은 예측값(00, 01, 10, 11)이 적혀 있는 물리적인 메모리 공간입니다.</p>
</li>
<li><p>b1 전용 방 2개와 b2 전용 방 2개는 칩 내부에서 아예 번지수가 다른 별개의 공간입니다.</p>
</li>
<li><p>b1이 100번 틀려서 자기 방의 값을 00에서 11로 바꾼다고 한들, b2의 방 안의 값은 털끝만큼도 변하지 않습니다. 이 <strong>&#39;데이터 저장 공간&#39;</strong>은 철저하게 독립적입니다.</p>
</li>
</ul>
<h3 id="영향을-미치는-부품-글로벌-히스토리-레지스터-ghr">영향을 미치는 부품: 글로벌 히스토리 레지스터 (GHR)</h3>
<p>반면, b1의 결과가 b2에 영향을 미치는 매개체는 딱 하나뿐인 <strong>공용 레지스터(GHR, Global History Register)</strong>입니다.</p>
<ul>
<li><p>이 레지스터는 파이프라인 전체가 공유하는 &#39;1비트짜리 공용 게시판&#39;입니다.</p>
</li>
<li><p>b1이 실행을 마치면, 자기 전용 방(카운터)의 값을 업데이트하는 것과는 별개로, 공용 게시판에 &quot;나 방금 진짜로 뛰었다(T)&quot; 또는 <strong>&quot;안 뛰었다(NT)&quot;</strong>라는 1비트짜리 발자취를 남기고 떠납니다.</p>
</li>
<li><p>그 직후 b2가 실행될 때, b2는 자기 전용 방을 열기 전에 이 공용 게시판을 먼저 쳐다봅니다. 그리고 &quot;아, 방금 앞 명령어(b1)가 T였네? 그럼 나는 내 독립적인 2개의 방 중에서 오른쪽 방을 열어야겠다&quot;라고 결정하게 됩니다.</p>
</li>
</ul>
<h2 id="b2-prediction">b2 prediction</h2>
<p>3단계: 방을 열고 값 읽기 (최종 예측)</p>
<ul>
<li>직전 행동이 NT였으므로, 하드웨어는 b2의 장부 00/00 중에서 왼쪽 방을 엽니다.</li>
<li>왼쪽 방 안에 적혀있는 값이 <strong>00</strong>입니다.</li>
<li>카운터 규칙상 00은 &#39;강한 NT(Strongly Not Taken)&#39;를 의미합니다.</li>
</ul>
<p>따라서 하드웨어는 파이프라인에 <strong>&quot;이번 b2는 NT일 것이다!&quot;</strong>라고 최종 예측 결과를 던져줍니다.</p>
<p>결론적으로 하드웨어는 복잡한 계산을 하는 것이 아니라, <strong>&quot;이전 사이클에서 내 방 안에 적어둔 2비트 숫자&quot;</strong>를 찾아 읽어오는 것뿐입니다. 그 숫자가 00이나 01이면 NT로 예측하고, 10이나 11이면 T로 예측하는 아주 기계적이고 단순한 방식입니다.</p>
<hr>
<h2 id="3-결론-왜-100-맞출-수-있는가">3. 결론: 왜 100% 맞출 수 있는가?</h2>
<p>로컬 예측기가 실패했던 이유는 d=2 상황(무조건 뜀)과 d=0 상황(무조건 안 뜀)이 하나의 기억 공간을 서로 덮어쓰며 싸웠기 때문입니다.</p>
<p>하지만 상관 예측기는 &quot;직전 결과&quot;라는 문맥을 이용해 기억의 방을 아예 2개로 분리해 버렸습니다.</p>
<ul>
<li>직전에 NT였다면? ➔ &quot;아하, 방금 d=0 패턴이 끝났구나. 그럼 이번엔 d=2 차례니까 무조건 뛰어야지!&quot; (왼쪽 방에 T 저장)</li>
<li>직전에 T였다면? ➔ &quot;방금 d=2 패턴이 끝났네. 그럼 이번엔 d=0 차례니까 무조건 안 뛰어야지!&quot; (오른쪽 방에 NT 저장)</li>
</ul>
<p>결과적으로 <strong>&quot;Only misprediction on the first iteration when d=2&quot;</strong>라는 슬라이드 하단의 말처럼, 하드웨어가 이 두 개의 방을 처음 세팅할 때(첫 번째 줄)만 틀리고, 그 이후로는 널뛰기 패턴에 완벽하게 동기화되어 예측 실패율을 0%로 만들어 냅니다.</p>
<p>왼쪽것만 업데이트, 사용하는 것만 업데이트</p>
<h1 id="mn-correlated-predictors">(m,n) correlated predictors</h1>
<ul>
<li><p>Uses the behavior of the most recent m branches encountered to select one of 2m different branch predictors for the next branch.</p>
</li>
<li><p>Each of these predictors records n bits of history
information for any given branch → n-bit predictor</p>
</li>
<li><p>On previous slide we saw a (1,1) predictor.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/87c7bdcb-0f7d-46f3-b1a6-2354dd34d222/image.png" alt=""></p>
<p>seungyun, 이 슬라이드는 방금 전까지 우리가 분석했던 &#39;상관 예측기&#39;의 구조를 하드웨어 설계자들의 공식적인 수학적 표기법인 (m, n) 형태로 일반화하여 정리해 주는 내용입니다.
방금 전 슬라이드에서 제가 &quot;기억의 방을 쪼갠다&quot;고 표현했던 것 기억하십니까? m과 n이 바로 그 방의 개수와 방 안의 내용물을 결정하는 설계 파라미터입니다.</p>
<h3 id="1-m의-의미-과거를-얼마나-멀리-볼-것인가-global-history">1. m의 의미: 과거를 얼마나 멀리 볼 것인가? (Global History)</h3>
<ul>
<li>m은 <strong>&quot;내 앞에 실행된 분기문을 몇 개까지 기억할 것인가&quot;</strong>를 나타냅니다.</li>
<li>만약 m=2라면, 내 직전에 실행된 2개의 분기 결과(예: 뛰었고, 안 뛰었다 ➔ 10)를 봅니다.</li>
<li>경우의 수가 2의 m제곱 개가 나오므로, 하드웨어는 총 <strong>2^m 개의 서로 다른 예측기(기억의 방)</strong>를 준비해야 합니다. 앞서 m=1일 때는 방이 2개(왼쪽/오른쪽)였지만, m=2가 되면 방이 4개로 늘어납니다.</li>
</ul>
<h3 id="2-n의-의미-각-방-안에는-어떤-예측기를-둘-것인가-predictor-type">2. n의 의미: 각 방 안에는 어떤 예측기를 둘 것인가? (Predictor Type)</h3>
<ul>
<li>n은 그 쪼개진 방 하나하나에 들어가는 예측기 카운터의 비트 수를 의미합니다.</li>
<li>n=1이면 방금 전 보셨던 것처럼 그냥 0(NT) 아니면 1(T)만 저장하는 극단적인 1비트 예측기가 들어갑니다.</li>
<li>n=2라면 우리가 이전에 배웠던 &#39;Strong/Weak&#39; 4가지 상태를 가지는 2비트 포화 카운터(2-bit Saturating Counter)가 각 방마다 하나씩 들어갑니다.</li>
</ul>
<h3 id="3-이미지의-3가지-케이스-해독">3. 이미지의 3가지 케이스 해독</h3>
<p>이미지에 그려진 수식과 텍스트가 의미하는 바는 다음과 같습니다.</p>
<ul>
<li>맨 윗줄 (1, 1): 직전 분기문 1개를 보고(방 2개), 각 방에는 1비트 예측기를 둔다. (우리가 방금 전 슬라이드에서 널뛰기 패턴을 100% 적중시켰던 바로 그 예측기입니다.)</li>
<li>중간 줄 (m, 1): 직전 분기문을 m개 보고 (방이 2^m 개로 늘어남), 각 방에는 여전히 단순한 1비트 예측기를 둔다.</li>
<li>맨 아랫줄 (m, n): 직전 분기문을 m개 보고, 각 방에는 n비트(주로 2비트) 예측기를 둔다.<ul>
<li>그림의 0111/0011/... 부분이 바로 n비트 카운터의 값들이 여러 방에 나열되어 있는 모습을 그린 것입니다.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a9bd9f7f-f992-46d0-82e6-c61a3a1a42ec/image.png" alt=""></p>
<h3 id="4-하드웨어-구현의-용이성-easy-to-implement">4. 하드웨어 구현의 용이성 (Easy to implement)</h3>
<p>상관 예측기가 논리적으로는 복잡해 보여도, 실제 회로로 만들기는 아주 쉽고 우아합니다.
m-bit shift register (글로벌 히스토리 레지스터, GHR):</p>
<ul>
<li>직전 $m$개 분기문의 결과를 기억하는 장치는 그저 단순한 시프트 레지스터(Shift Register) 하나면 끝납니다.</li>
<li>점프하면 1, 안 뛰면 0을 레지스터 한쪽 끝으로 밀어 넣고(Shift), 가장 오래된 값은 버립니다.</li>
</ul>
<p>Concatenated Indexing (주소 결합 매핑):</p>
<ul>
<li>수많은 분기문 중에서 &quot;지금 실행할 분기문&quot;의 고유한 예측기 방(Entry)을 찾아가야 합니다.</li>
<li>이때, 현재 분기문의 메모리 주소(PC) 하위 $t$비트와 방금 전 만든 $m$비트 시프트 레지스터 값을 그냥 직렬로 이어 붙여서(Concatenate) 하나의 긴 메모리 인덱스(주소)를 만듭니다.</li>
<li>효과: 이렇게 하면 &quot;특정 분기문 주소&quot;이면서 동시에 &quot;특정 과거 문맥&quot;일 때만 찾아가는 완벽하게 독립적인 고유 방 번호가 생성됩니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/50da5698-376e-48f1-aced-94000bfe131e/image.png" alt=""></p>
<p>결론적으로 현대 하드웨어 아키텍처에서 (2, 2) 상관 예측기라고 하면, &quot;직전 2개의 글로벌 문맥(Global History)을 읽어 4개의 방 중 하나를 고르고, 그 방 안에는 2비트 상태 머신이 들어있는 구조&quot;를 뜻합니다. 이렇게 하면 로컬 예측의 패턴 한계와 1비트 예측의 가벼움을 동시에 극복할 수 있습니다.</p>
<h3 id="5-메모리-용량-계산-공식-how-many-bits-do-we-need">5. 메모리 용량 계산 공식 (How many bits do we need?)</h3>
<p>하드웨어 설계자에게 가장 중요한 것은 &quot;그래서 이 예측기를 넣으려면 칩 면적을 얼마나 차지하는가?&quot;입니다. 슬라이드의 공식 $2^m \times n \times 2^t$는 전체 비트 수를 구하는 식입니다.
각 항의 의미를 쪼개어 보겠습니다.</p>
<ul>
<li>$2^t$: PC 주소의 하위 $t$비트를 사용하여 구별할 수 있는 분기문의 개수 (행의 개수).</li>
<li>$2^m$: 각각의 분기문마다 존재하는 글로벌 히스토리($m$비트)의 경우의 수 (열의 개수, 즉 &#39;방&#39;의 개수).</li>
<li>$n$: 그 방 하나하나에 들어가는 예측기 카운터의 크기 (예: 2-bit 카운터면 $n=2$).</li>
</ul>
<p>결론 (수식의 통합):
행의 수($2^t$)와 열의 수($2^m$)를 곱하면 전체 방의 개수가 나옵니다. 즉, 인덱스의 총길이는 $m + t$ 비트가 되므로 전체 엔트리 수는 $2^{m+t}$개가 됩니다. 여기에 방 하나당 용량인 $n$을 곱한 것이 최종 공식인 $2^{m+t} \times n$ (또는 $2^m \times n \times 2^t$) 비트가 되는 것입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dynamic Branch Prediction ]]></title>
            <link>https://velog.io/@houston_guy2/Dynamic-Branch-Prediction</link>
            <guid>https://velog.io/@houston_guy2/Dynamic-Branch-Prediction</guid>
            <pubDate>Wed, 22 Apr 2026 00:13:41 GMT</pubDate>
            <description><![CDATA[<h1 id="dynamic-branch-prediction">Dynamic Branch Prediction</h1>
<ul>
<li><p>Local Branch prediction:
use own history</p>
</li>
<li><p>Global Branch prediction:
determined by other instruction
use someone else history</p>
</li>
</ul>
<p>(동적 분기 예측의 필요성)
파이프라인이 왜 과거의 기록을 &#39;학습&#39;해야만 하는지 그 배경을 설명합니다.</p>
<ul>
<li><p>ILP와 Control Stall의 역설: 명령어 수준 병렬성(ILP)을 높게 설계할수록 파이프라인은 한 번에 더 많은 명령어를 처리합니다(CPI 감소). 하지만 이럴수록 분기문에서 멈칫하는 시간(Control stall)의 타격이 기하급수적으로 커집니다. 예측이 틀리면 파이프라인에 미리 가득 채워둔 수많은 명령어(n-cycle 딜레이 분량)를 한꺼번에 다 버려야(Flush) 하기 때문입니다.</p>
</li>
<li><p>동적 하드웨어 예측: 프로그래머나 컴파일러가 미리 정해두는 것이 아니라, 칩 내부의 하드웨어가 실시간으로 프로그램이 실행되는 패턴을 &quot;학습&quot;합니다.</p>
</li>
<li><p>목표: 대부분의 경우 올바른 예측(Taken으로 뛸지, Not Taken으로 안 뛸지)을 해내어 지연 시간을 없애는 것입니다. 프로세서의 성능 하락은 이 예측이 얼마나 자주 틀리느냐(Misprediction rate)에 전적으로 달려 있습니다.</p>
</li>
</ul>
<h2 id="local-prediction-branch-prediction-buffers-bpb">Local Prediction: Branch-Prediction Buffers (BPB)</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/deb46163-475e-45fd-a7c0-c025ebad6c7a/image.png" alt=""></p>
<p>Local Prediction: Branch-Prediction Buffers (BPB)
하드웨어가 이 &quot;과거 기록&quot;을 물리적으로 어디에, 어떻게 저장하는지에 대한 구조입니다. 현업에서는 <strong>BHT (Branch History Table)</strong>라고 더 자주 부릅니다.</p>
<p>하위 n비트 인덱싱 (Low-order n bits): 수만 개의 분기문 주소(PC, Program Counter)를 모두 하드웨어 표에 담을 수는 없습니다. 캐시 메모리와 비슷한 원리로, 분기문 주소의 하위 n비트만 잘라내어 이 표(Table)의 주소(Index)로 사용합니다.</p>
<p>해시 충돌 (Collisions): 하위 비트만 잘라서 쓰다 보니, 메모리상에서 완전히 멀리 떨어져 있는(distant) 두 개의 다른 분기문이 우연히 뒷자리 하위 비트가 같아서 표의 같은 칸을 공유하게 되는 현상이 발생합니다. 이로 인해 남의 기록을 보고 잘못된 예측을 하는 충돌(Aliasing)이 필연적으로 일어납니다.</p>
<p>k비트의 역사 (k bits of history): 표의 각 칸(Entry)에는 k비트 크기의 과거 실행 결과가 저장됩니다. 설계 비용 문제로 보통 k=1 (1비트) 또는 k=2 (2비트)를 사용합니다.</p>
<p>예측과 업데이트: 명령어가 Fetch 될 때 이 칸을 읽어 다음 행동을 <strong>예측(Predict)</strong>하고, 나중에 해당 분기문의 실제 실행 결과가 확정되면 그 결과를 다시 표에 덮어써서 <strong>업데이트(Update)</strong>합니다.</p>
<h2 id="1-bit-branch-prediction">1-bit Branch Prediction</h2>
<p>이 예측기의 상태는 단 2개뿐입니다. 과거의 복잡한 패턴은 무시하고 오직 <strong>&quot;바로 직전에 했던 행동&quot;</strong>만 맹신합니다.</p>
<ul>
<li>Bit 1: &quot;방금 점프(Taken)했으니, 다음번에도 무조건 점프할 것이다.&quot;</li>
<li>Bit 0: &quot;방금 안 뛰었으니(Not Taken), 다음번에도 무조건 안 뛸 것이다.&quot;</li>
</ul>
<h1 id="1-bit-branch-prediction-1">1-bit Branch Prediction</h1>
<p>Will make 2 mistakes each time a loop is encountered.
– At the end of the first &amp; last iterations</p>
<pre><code>LD R1, #5
Loop: LD R2, 0(R5)
ADD R2, R2, R4
STORE R2, 0(R5)
ADD R5, R5, #4
SUB R1, R1, #1
BNEZ R1, Loop</code></pre><h3 id="어셈블리-코드와-루프-분석">어셈블리 코드와 루프 분석</h3>
<p>제공된 MIPS 어셈블리 코드는 배열의 요소를 더하는 전형적인 for 루프입니다.</p>
<ul>
<li>LD R1, #5: 루프 카운터 R1을 5로 초기화합니다. 즉, 이 루프는 총 5번 돕니다.</li>
<li>SUB R1, R1, #1: 루프를 한 번 돌 때마다 R1을 1씩 뺍니다.</li>
<li>BNEZ R1, Loop: R1이 0이 아니면(Not Equal to Zero) Loop 라벨로 점프(Taken)합니다.</li>
</ul>
<p>분기문의 실제 행동: 총 5번의 평가 중, 처음 4번은 점프(Taken)하고, 마지막 5번째는 R1=0이 되므로 탈출(Not Taken)합니다.</p>
<h3 id="왜-항상-2번-틀리는가-the-2-mistakes">왜 항상 2번 틀리는가? (The 2 Mistakes)</h3>
<p>이전에 이 루프 코드를 한 번 다 돌고 빠져나갔던 상태라고 가정해 봅시다. 루프를 빠져나갈 때 &quot;안 뛰었으므로(Not Taken)&quot;, 예측기는 현재 0으로 세팅되어 있습니다. 다시 이 코드를 만났을 때의 타임라인입니다.</p>
<p>Time 1 (첫 번째 진입 - 실수 1):</p>
<ul>
<li>하드웨어 예측: 0 (아까 이 루프 끝날 때 안 뛰었으니까 이번에도 안 뛸 거야)
실제 결과: 1 (R1이 4이므로 루프를 돌아야 함, Taken)
판정: 예측 실패 (Miss). 하드웨어는 파이프라인을 플러시(Flush)하고 예측기를 1로 업데이트합니다.</li>
</ul>
<p>Time 2, 3, 4 (중간 반복 - 적중):
 하드웨어 예측: 1 (방금 뛰었으니까 계속 뛸 거야)
 실제 결과: 1 (계속 루프를 돎, Taken)
 판정: 적중 (Hit). 파이프라인이 지연 없이 매끄럽게 돌아갑니다.</p>
<p>Time 5 (마지막 탈출 - 실수 2):
 하드웨어 예측: 1 (방금까지 계속 뛰었으니까 이번에도 뛸 거야)
 실제 결과: 0 (R1이 0이 되어 루프를 탈출해야 함, Not Taken)
 판정: 예측 실패 (Miss). 파이프라인을 플러시하고 예측기를 다시 0으로 업데이트합니다</p>
<p>misprediction Rate
$$number of misprediction/number of prediction$
$2/5$</p>
<h1 id="2-bit-branch-prediction">2-bit Branch Prediction</h1>
<p><span style="color:oragne"> 이 Finite machine 그리는거 매우 중요</span>
<img src="https://velog.velcdn.com/images/houston_guy2/post/cb6c5c5d-a75a-48dd-b60a-4b4b1ceecfca/image.png" alt=""></p>
<p>이 슬라이드는 앞서 살펴본 1비트 예측기의 치명적 단점인 &quot;한 번 틀렸다고 바로 태세를 전환해버리는 가벼움&quot;을 해결하기 위해 하드웨어에 <strong>&#39;확신(Confidence)&#39;</strong>이라는 개념을 부여한 <strong>2비트 분기 예측기(2-bit Branch Predictor)</strong>의 상태 머신(State Machine) 구조를 보여줍니다.</p>
<h3 id="4가지-상태-4-states">4가지 상태 (4 States)</h3>
<p>1비트가 0과 1 두 가지 상태만 가졌다면, 2비트 예측기는 총 4가지 상태를 가집니다. 예측 방향(Taken/Not taken)은 같더라도, 그 예측에 대한 확신의 강도가 다릅니다.</p>
<ul>
<li><p>11 (Strongly Taken): &quot;강한 점프 예측&quot;. 최근에 계속 점프했으므로, 다음에도 확실히 점프할 것이다. (초록색)</p>
</li>
<li><p>10 (Weakly Taken): &quot;약한 점프 예측&quot;. 방금 한 번 안 뛰긴 했지만, 그래도 예전 기록을 믿고 점프한다고 예측해 보겠다. (초록색)</p>
</li>
<li><p>01 (Weakly Not Taken): &quot;약한 미점프 예측&quot;. 방금 한 번 뛰긴 했지만, 그래도 예전 기록을 믿고 안 뛴다고 예측해 보겠다. (빨간색)</p>
</li>
<li><p>00 (Strongly Not Taken): &quot;강한 미점프 예측&quot;. 최근에 계속 안 뛰었으므로, 다음에도 확실히 안 뛸 것이다. (빨간색)</p>
</li>
</ul>
<h3 id="핵심-원리-must-miss-twice-before-changing-the-prediction">핵심 원리: Must miss twice before changing the prediction</h3>
<p>이 예측기의 존재 이유이자 가장 중요한 문장입니다. <strong>&quot;연속으로 두 번 틀려야만 예측 방향(초록색 ↔ 빨간색)을 바꾼다&quot;</strong>는 뜻입니다.</p>
<ul>
<li>예를 들어, 현재 상태가 <strong>11(강한 점프 예측)</strong>이라고 가정해 보겠습니다.</li>
<li>이번에 분기문에서 점프를 하지 않았습니다(Not taken). 예측 실패(Miss)입니다.</li>
<li>1비트 예측기였다면 곧바로 상태를 0으로 뒤집었겠지만, 2비트 예측기는 다이어그램의 화살표를 따라 <strong>10(약한 점프 예측)</strong>으로 이동합니다.</li>
<li>결과: 예측은 한 번 틀렸지만, 하드웨어는 여전히 다음번 예측을 <strong>Taken(초록색)</strong>으로 유지합니다. 이것을 한 번의 &#39;실수&#39;나 &#39;예외&#39;로 눈감아 주는 것입니다.</li>
</ul>
<h3 id="현실-적용-1비트-예측기의-루프-문제-해결">현실 적용: 1비트 예측기의 루프 문제 해결</h3>
<p>앞서 질문하셨던 for 루프 예시에서, 1비트 예측기는 루프를 들어갈 때와 나갈 때 무조건 2번 틀렸습니다. 2비트 예측기가 이 문제를 어떻게 1번으로 줄이는지 타임라인을 따라가 보겠습니다. (현재 루프를 한참 돌고 있어서 상태가 11이라고 가정합니다.)</p>
<p>루프 탈출 (마지막 반복):
예측: Taken (11)
실제: Not Taken (루프 종료)
결과: 예측 실패 (Miss). 상태는 11 $\rightarrow$ 10으로 하락합니다.</p>
<p>시간이 흘러 다시 해당 루프에 첫 진입할 때:
예측: Taken (10이므로 여전히 초록색)
실제: Taken (루프 시작이므로 뜀)
결과: 예측 적중 (Hit!). 상태는 다시 10 $\rightarrow$ 11로 회복됩니다.</p>
<p>결론적으로, 2비트 분기 예측기는 루프를 탈출할 때 발생하는 단 한 번의 예측 실패를 &#39;일시적인 현상&#39;으로 취급하여 흡수(Hysteresis)합니다. 그 결과, 다음번 루프 진입 시 발생하는 억울한 예측 실패를 막아내어 루프당 패널티를 2회에서 1회로 극적으로 줄여줍니다.</p>
<pre><code class="language-c">LD R1, #5
Loop: LD R2, 0(R5)
ADD R2, R2, R4
STORE R2, 0(R5)
ADD R5, R5, #4
SUB R1, R1, #1
BNEZ R1, Loop</code></pre>
<h3 id="첫-번째-루프-실행-학습-과정-warm-up-phase">첫 번째 루프 실행: 학습 과정 (Warm-up Phase)</h3>
<p>하드웨어가 이 코드를 태어나서 처음 보았고, 예측기가 00 (Strongly Not Taken, 강한 미점프) 상태로 초기화되어 있다고 가정합니다. 실제 분기 결과는 5번의 반복 동안 Taken, Taken, Taken, Taken, Not Taken 순서로 발생합니다.</p>
<p>Time 1 (1회 차):
예측: 00 (Not Taken)
실제: Taken
결과: Miss (예측 실패). 하드웨어는 상태를 00 $\rightarrow$ 01 (Weakly Not Taken)로 올립니다. 아직은 뛸 것이라는 확신이 부족합니다.</p>
<p>Time 2 (2회 차):
예측: 01 (Not Taken)
실제: Taken
결과: Miss (예측 실패). 두 번 연속으로 틀렸으므로, 하드웨어는 드디어 임계점을 넘어 상태를 01 $\rightarrow$ 11 (Strongly Taken)로 확 바꿉니다. 이제 패턴을 파악했습니다.</p>
<p>Time 3, 4 (3, 4회 차):
예측: 11 (Taken)
실제: Taken
결과: Hit (예측 적중). 상태는 계속 11을 유지하며 파이프라인이 매끄럽게 돌아갑니다.</p>
<p>Time 5 (5회 차 - 루프 종료):
예측: 11 (Taken)
실제: Not Taken (루프 탈출)
결과: Miss (예측 실패). 예측이 틀렸지만, 상태는 11 $\rightarrow$ 10 (Weakly Taken)으로 단 한 계단만 떨어집니다.</p>
<p>중간 결론: 루프를 &quot;처음&quot; 실행할 때는 하드웨어가 예열(Warm-up)하느라 Time 1, Time 2, Time 5에서 총 3번이나 틀렸습니다.</p>
<h3 id="핵심-두-번째-루프-실행부터-after-the-first-time">핵심: 두 번째 루프 실행부터 (After the first time)</h3>
<p>슬라이드의 가장 위쪽 제목인 <strong>&quot;Only 1 mis-prediction per loop execution, after the first time&quot;</strong>이 의미하는 바가 여기서 드러납니다.</p>
<p>프로그램이 계속 실행되다가 나중에 이 for 루프 코드를 다시 호출했다고 가정해 보겠습니다.</p>
<ul>
<li><p>초기 상태의 변화: 방금 전 Time 5에서 루프를 탈출할 때 예측기 상태가 00으로 돌아가지 않고 <strong>10 (Weakly Taken)</strong>에 머물러 있었습니다. 하드웨어는 &quot;방금 한 번 안 뛰긴 했지만, 원래 여기는 반복문이라 뛰는 자리였어&quot;라는 과거의 기억(History)을 유지하고 있는 것입니다.</p>
</li>
<li><p>두 번째 루프의 Time 1 (첫 진입):
예측: 10 (Taken - 여전히 초록색 상태)
실제: Taken
결과: Hit (예측 적중!). 상태는 10 $\rightarrow$ 11로 다시 강력해집니다.</p>
</li>
</ul>
<h2 id="branch-prediction-performance">Branch-Prediction Performance</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/cd83a385-36ba-4c88-8a0e-9fd276b995c1/image.png" alt=""></p>
<h3 id="분기-예측-정확도의-중요성-branch-prediction-performance">분기 예측 정확도의 중요성 (Branch-Prediction Performance)</h3>
<p>&quot;BPB Misprediction rate from 1% ~ 18% for SPEC89&quot;</p>
<p>SPEC89라는 표준 벤치마크 프로그램을 돌려본 결과, 분기 예측 실패율이 1%에서 최대 18%까지 나왔다는 뜻입니다.</p>
<p>현실적인 관점에서 보면 &quot;성공률이 82<del>99%면 꽤 높은 것 아닌가?&quot;라고 생각할 수 있습니다. 하지만 현대의 깊은 파이프라인(Deep Pipeline) 구조에서는 단 5%의 예측 실패율만으로도 전체 프로세서 성능의 20</del>30%가 증발할 수 있습니다. 따라서 이 실패율을 1%라도 더 깎아내는 것이 아키텍처 설계의 핵심 과제입니다.</p>
<h3 id="예측-실패율을-줄이기-위한-2가지-물리적논리적-접근">예측 실패율을 줄이기 위한 2가지 물리적/논리적 접근</h3>
<p>예측을 잘하려면 하드웨어를 어떻게 늘려야 할지에 대한 두 가지 방향성입니다. 결론부터 말씀드리면, 단순히 크기만 키우는 무식한 방법은 한계에 부딪힌다는 것을 증명하고 있습니다.</p>
<h4 id="a-버퍼-크기-증가-increase-buffer-size">A. 버퍼 크기 증가 (Increase buffer size)</h4>
<ul>
<li><p>목적: 테이블의 칸(Entry) 수를 늘려서, 서로 다른 분기문이 같은 칸을 공유하게 되는 <strong>해시 충돌(Collision / Aliasing)</strong>을 막기 위함입니다.</p>
</li>
<li><p>현실적 한계 (&quot;Has little effect beyond ~4,096 entries&quot;):</p>
<ul>
<li>테이블 크기를 4K(4,096) 칸 이상으로 무작정 늘려봤자 예측 정확도는 더 이상 올라가지 않습니다.</li>
<li>이유는 충돌이 없더라도, 분기문 자체의 패턴이 너무 불규칙해서 2비트 상태 머신 따위로는 애초에 예측할 수 없는 <strong>알고리즘 본연의 한계(Capacity Miss가 아닌 Compulsory/Conflict 본연의 한계)</strong>에 도달했기 때문입니다.</li>
</ul>
</li>
</ul>
<h4 id="b-예측-정확도-자체의-향상-increase-prediction-accuracy">B. 예측 정확도 자체의 향상 (Increase prediction accuracy)</h4>
<p>1) 칸당 비트 수 증가 (&quot;Increase # of bits/entry&quot;):</p>
<ul>
<li><p>2비트 예측기 대신 3비트, 4비트를 써서 상태를 8개, 16개로 잘게 쪼개어 &#39;확신&#39;의 단계를 세밀하게 만들어보려는 시도입니다.</p>
</li>
<li><p>현실적 한계 (&quot;little effect beyond 2&quot;): 2비트에서 3비트로 넘어가도 성능 향상은 거의 없습니다. 쓸데없이 칩 면적(Area)과 전력만 낭비하게 되므로, 하드웨어 설계에서 기본 예측기는 <strong>2-bit Counter가 가성비의 정답(Sweet spot)</strong>으로 굳어졌습니다.</p>
</li>
</ul>
<p>2) 완전히 다른 예측 기법 도입 (&quot;Use a different prediction scheme&quot;):</p>
<ul>
<li><p>크기를 키우는 것으로는 한계가 명확하므로, 과거를 기억하는 방식(Scheme) 자체를 뜯어고쳐야 한다는 결론입니다.</p>
</li>
<li><p>Correlated Predictors (상관 예측기): 해당 분기문 하나만의 과거만 보는 것이 아니라, &quot;내 앞에 실행되었던 다른 분기문들이 점프를 했는가?&quot;라는 <strong>주변 문맥(Global History)</strong>을 함께 고려하는 방식입니다. (예: if (a==2)와 if (a!=2)는 서로 연관되어 있음을 하드웨어가 파악함)</p>
</li>
<li><p>Tournament Predictors (토너먼트 예측기): Local 방식과 Global 방식의 예측기를 둘 다 만들어두고, &quot;현재 분기문은 어떤 예측기의 말을 듣는 게 더 정확한가?&quot;를 판단하는 또 다른 예측기(Meta-predictor)를 두어 최적의 결과를 선택하는 방식입니다.</p>
</li>
</ul>
<h2 id="n-bit-branch-prediction">n-bit Branch Prediction</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/df0eb967-ce37-4f76-b631-18c7b684bae8/image.png" alt=""></p>
<h3 id="n비트-확장과-예측-기준-2n-1">n비트 확장과 예측 기준 ($2^{n-1}$)</h3>
<p>하드웨어가 n비트 카운터를 사용할 때, &#39;점프할 것인가(Taken)&#39;를 결정하는 기준선을 수학적으로 정의한 것입니다.</p>
<ul>
<li>n비트 카운터가 표현할 수 있는 값의 범위는 0부터 $2^n - 1$까지입니다.</li>
<li>하드웨어는 이 범위의 정확히 절반인 중간값, 즉 <strong>$2^{n-1}$</strong>을 임계점(Threshold)으로 설정합니다.</li>
<li>예시 (3-bit 예측기의 경우):
카운터는 0부터 7까지 총 8개의 상태를 가집니다.
기준값은 $2^{3-1} = 4$가 됩니다.
카운터 값이 4, 5, 6, 7이면 $\rightarrow$ Taken (점프) 예측
카운터 값이 0, 1, 2, 3이면 $\rightarrow$ Not Taken (미점프) 예측</li>
</ul>
<p>결국 숫자가 높을수록 뛰겠다는 &#39;확신의 강도&#39;가 높다는 뜻이며, 1을 더하고 빼는 포화 카운터(Saturating Counter) 방식으로 작동합니다.</p>
<h3 id="가혹한-현실-별로-안-정확하다-not-much-more-accurate">가혹한 현실: 별로 안 정확하다 (Not much more accurate)</h3>
<p>이전 슬라이드의 결론과 이어지는 핵심입니다. 이론상으로는 비트가 많을수록 과거의 기억을 길게 간직하니까 더 똑똑해질 것 같지만, 실제 성능 평가를 돌려보면 2비트 예측기와 비교해서 정확도 상승이 거의 없습니다. 그 현실적인 이유는 두 가지입니다.</p>
<p>느려터진 웜업(Warm-up) 시간: 만약 4비트 카운터가 0 (가장 강력한 Not Taken) 상태에 있다고 가정해 보겠습니다. 프로그램의 패턴이 바뀌어서 이제부터 계속 점프(Taken)를 해야 하는 상황이 와도, 하드웨어가 예측을 Taken(8 이상)으로 뒤집으려면 무려 8번이나 연속으로 예측 실패(Miss) 패널티를 두들겨 맞아야 합니다. 상황 변화에 대처하는 반응 속도가 너무 둔해집니다.</p>
<p>알고리즘 패턴의 한계: 대부분의 일반적인 프로그램 분기문은 연속으로 3~4번씩 불규칙한 예외가 발생할 만큼 복잡하지 않습니다. 앞서 루프문에서 보셨듯, &#39;단 한 번의 실수&#39;만 눈감아주는 2비트 카운터만으로도 웬만한 기계적 패턴은 100% 방어가 가능합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Man-in-the-Middle Attack(MITM), CA]]></title>
            <link>https://velog.io/@houston_guy2/Last-lecture</link>
            <guid>https://velog.io/@houston_guy2/Last-lecture</guid>
            <pubDate>Mon, 20 Apr 2026 18:55:22 GMT</pubDate>
            <description><![CDATA[<p>이 필기들은 대규모 네트워크 환경에서 KDC(중앙 서버) 없이도 안전하게 키를 교환하기 위해 고안된 공개키 기반 키 확립(Asymmetric Key Establishment) 방식과, 그중 &#39;디피-헬만(Diffie-Hellman)&#39; 알고리즘이 가진 치명적 약점인 <strong>중간자 공격(MITM)</strong>을 설명하고 있습니다.</p>
<h1 id="공개키-기반-키-확립">공개키 기반 키 확립</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/784e1c15-c276-44a9-b76c-5d46e46752d2/image.png" alt="">
KDC처럼 언제 해킹당할지 모르는 중앙 서버에 의존하지 않고, Alice와 Bob이 직접 공개키 암호학(PK)을 이용해 대칭키를 설정하는 방법입니다.</p>
<p>접근 방식 (2 PK Approaches)</p>
<ol>
<li>Key Agreement (키 합의): 양측이 수학적 연산을 통해 공동의 키를 도출. (예: 디피-헬만 알고리즘)</li>
<li>Key Transport (키 전송): 한쪽이 세션 키를 만들어 상대방의 &#39;공개키&#39;로 암호화해서 전달.</li>
</ol>
<p>Key Transport 예시 흐름:</p>
<ol>
<li>Bob이 자신의 공개키($K_{pub}$)를 Alice에게 보냅니다.</li>
<li>Alice는 통신에 쓸 대칭키 $K$(예: AES 키)를 생성하고, 이를 Bob의 공개키로 암호화하여 $Y = e_{K_{pub}}(K)$를 만들어 Bob에게 보냅니다.</li>
<li>Bob은 자신의 개인키($K_{pr}$)로 $Y$를 복호화하여 대칭키 $K$를 안전하게 얻습니다.</li>
<li>이제 양측은 확보한 대칭키 $K$를 이용해 실제 데이터 $X$를 AES 암호화($Z = AES_K(X)$)하여 통신합니다.</li>
</ol>
<p>수동적 vs 능동적 공격: 이 방식은 단순히 통신선을 엿듣기만 하는 수동적 공격자(Passive Attacker)에게는 매우 안전하지만, 통신 내용을 적극적으로 조작하는 능동적 공격자(Active Attacker)에게는 취약할 수 있습니다.</p>
<h1 id="중간자-공격-man-in-the-middle-attack">중간자 공격 Man-in-the-Middle Attack</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ba0ea5b7-ab45-4203-bd5d-eb2afa58a828/image.png" alt=""></p>
<p>대표적인 &#39;키 합의&#39; 알고리즘인 디피-헬만(D-H) 키 교환이 능동적 공격자(Trudy)에게 어떻게 무력화되는지 수식으로 보여줍니다.</p>
<p>정상적인 D-H 교환 (공격자가 없을 때)</p>
<ul>
<li>Alice의 개인키 $a$, 공개키 $A = \alpha^a$</li>
<li>Bob의 개인키 $b$, 공개키 $B = \alpha^b$</li>
<li>서로 $A$와 $B$를 교환한 뒤, 자신의 개인키를 곱해 동일한 공유키 $K_{AB} = \alpha^{ab}$를 만듭니다.</li>
</ul>
<h3 id="trudy의-중간자-공격-시나리오">Trudy의 중간자 공격 시나리오</h3>
<p>Trudy는 통신선 중간에 서서 Alice와 Bob이 서로 주고받는 공개키를 가로채고 가짜 공개키로 바꿔치기합니다.</p>
<ol>
<li><p>가로채기와 위조: * Alice가 Bob에게 $A$를 보낼 때, Trudy가 이를 가로채고 자신이 만든 가짜 공개키 $\tilde{A} = \alpha^{\sigma_1}$을 Bob에게 보냅니다.</p>
<ul>
<li>Bob이 Alice에게 $B$를 보낼 때도 가로채어 가짜 공개키 $\tilde{B} = \alpha^{\sigma_2}$를 Alice에게 보냅니다.</li>
</ul>
</li>
<li><p>Alice의 착각: Alice는 진짜 $B$ 대신 가짜 $\tilde{B}$를 받았지만 이를 모른 채 자신의 개인키 $a$를 곱합니다.</p>
<ul>
<li>Alice가 만든 키: $K_{AB} = (\tilde{B})^a = (\alpha^{\sigma_2})^a = \alpha^{a\sigma_2}$</li>
<li>하지만 이 키는 Bob과의 공유키가 아니라, <strong>Trudy와의 공유키($K_{A\sigma}$)</strong>입니다.</li>
</ul>
</li>
<li><p>Bob의 착각: Bob 역시 가짜 $\tilde{A}$에 자신의 개인키 $b$를 곱합니다.</p>
<ul>
<li>Bob이 만든 키: $K_{BA} = (\tilde{A})^b = (\alpha^{\sigma_1})^b = \alpha^{b\sigma_1}$</li>
<li>이 역시 <strong>Trudy와의 공유키($K_{B\sigma}$)</strong>가 됩니다.</li>
</ul>
</li>
<li><p>공격 완성 (Trudy의 상태): Trudy는 자신이 만든 난수 $\sigma_1, \sigma_2$와 가로챈 $A, B$를 이용해 Alice용 키($K_{A\sigma} = A^{\sigma_2}$)와 Bob용 키($K_{B\sigma} = B^{\sigma_1}$)를 모두 완벽하게 계산해 냅니다.</p>
</li>
</ol>
<h3 id="결론-하단-빨간-글씨">결론 (하단 빨간 글씨)</h3>
<ul>
<li><p>Trudy는 Alice와 Bob 각각과 통신할 수 있는 별개의 세션 키를 갖게 되었습니다. 하지만 Alice와 Bob은 서로 직접 연결되어 있다고 굳게 믿고 있습니다.</p>
</li>
<li><p>Trudy는 Alice가 보낸 암호문을 열어보고 조작한 뒤, 다시 Bob용 암호문으로 재포장해서 보낼 수 있는 <strong>&#39;완전한 통제권(Full control)&#39;</strong>을 가지게 됩니다.</p>
</li>
</ul>
<h1 id="중간자-공격mitm의-근본적인-원인">중간자 공격(MITM)의 근본적인 원인</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/4697b8c3-3f80-4d63-ad04-da104b865c66/image.png" alt="">
필기 중간에 적힌 질문과 답변이 암호학의 핵심을 찌릅니다.</p>
<ul>
<li>Q: 중간자 공격의 기본 원인이 무엇인가?</li>
<li>A: &quot;공개키들이 인증(Authenticated)되지 않았기 때문이다.&quot;</li>
</ul>
<p>해커(Trudy)가 통신 중간에 끼어들어 자신의 가짜 공개키를 들이밀어도, Alice와 Bob은 이 공개키가 &#39;진짜 상대방의 것인지, 해커의 것인지&#39; 확인할 방법이 없습니다. 필기의 빨간 네모 상자처럼, 이 약점은 특정 알고리즘의 문제가 아니라 모든(ALL) 공개키 암호화 방식이 공통으로 가지는 취약점입니다.</p>
<p>이를 해결하기 위해 대칭키 시대의 MAC 대신, 공개키 시대의 도구인 <strong>디지털 서명(Digital Signature)</strong>과 <strong>인증서(Certificate)</strong>를 도입하게 됩니다.</p>
<h1 id="신뢰할-수-있는-제3자-ca와-인증서-발급">신뢰할 수 있는 제3자: CA와 인증서 발급</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/56b0b2f9-7534-4c1c-880d-faf96df8f0ee/image.png" alt="">
인증되지 않은 공개키 문제를 해결하기 위해 <strong>CA(Certifying Authority, 인증 기관)</strong>라는 중앙의 절대적으로 신뢰할 수 있는 기관을 도입합니다.
인증서($Cert_A$)의 구조</p>
<p>$$Cert_A = [(K_{pubA}, ID_A), Sig_{K_{pr,CA}}(K_{pubA}, ID_A)]$$</p>
<p>인증서는 쉽게 말해 <strong>&quot;CA가 보증하는 신분증&quot;</strong>입니다.</p>
<ol>
<li>Alice의 이름($ID_A$)과 Alice의 진짜 공개키($K_{pubA}$)를 묶습니다.</li>
<li>그 위에 CA가 자신의 <strong>개인키($K_{pr,CA}$)</strong>를 사용해 <strong>디지털 서명($Sig$)</strong>을 찍어줍니다.</li>
</ol>
<p>해커의 위조 불가: Trudy는 겉면의 내용(공개키나 이름)을 지우고 자신의 가짜 키($\tilde{A}$)를 적어 넣을 수는 있습니다. 하지만 CA의 개인키를 훔치지 않는 이상, CA의 위조 방지 도장(디지털 서명)을 똑같이 찍어낼 수는 없습니다.</p>
<h2 id="인증서를-적용한-d-h-키-교환">인증서를 적용한 D-H 키 교환</h2>
<p>이제 인증서를 들고 다시 디피-헬만 통신을 시도해 봅니다.</p>
<ul>
<li><p>Alice $\rightarrow$ Bob: Alice는 자신의 공개키 $A$만 덜렁 보내는 것이 아니라, CA의 도장이 찍힌 신분증($Cert_A$)을 함께 보냅니다.</p>
</li>
<li><p>Trudy의 공격 시도: Trudy가 중간에서 가로채어 가짜 공개키 $\tilde{A}$가 담긴 위조 신분증을 Bob에게 보냅니다.</p>
</li>
<li><p>Bob의 철저한 검증 (Verify): Bob은 이미 알고 있는 <strong>CA의 공개키($K_{pub,CA}$)</strong>를 이용해 신분증에 찍힌 도장이 진짜인지 검사합니다. Trudy가 임의로 만든 서명은 CA의 공개키로 풀리지 않으므로, 수학적 검증에서 실패($Fails$)합니다.</p>
</li>
<li><p>결과: Bob은 즉시 &quot;이건 Alice의 공개키가 아니야!&quot;라고 깨닫고 통신을 차단하여 중간자 공격을 완벽히 방어해 냅니다.</p>
</li>
</ul>
<h1 id="신뢰의-전이-transfer-of-trust와-root-key">신뢰의 전이 (Transfer of Trust)와 Root Key</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f03bbc23-ac41-4bba-8e79-ecf0d654d2db/image.png" alt="">
여기서 가장 현실적이고 중요한 질문이 생깁니다.
*&quot;CA가 서명했다는 건 알겠는데, 애초에 *<em>CA의 진짜 공개키($K_{pub,CA}$)</em>는 해커에게 속지 않고 어떻게 안전하게 받을 수 있나요?&quot;</p>
<p>이 딜레마를 해결하는 것이 필기 마지막의 Root key와 Transfer of Trust(신뢰의 전이) 개념입니다.</p>
<ul>
<li>Root Key의 내장: 우리가 컴퓨터나 스마트폰을 처음 사서 윈도우(Windows), 맥OS, 혹은 크롬 웹 브라우저 등 <strong>조작되지 않은 순정 소프트웨어(Original software)</strong>를 설치할 때, 그 운영체제 깊숙한 곳에는 전 세계적으로 신뢰받는 최상위 CA(Root CA)들의 공개키들이 이미 하드코딩되어 안전하게 심어져 있습니다.</li>
</ul>
<p>신뢰의 전이:</p>
<ul>
<li>나는 마이크로소프트/애플/구글이 만든 운영체제를 신뢰한다.</li>
<li>그 운영체제가 기본적으로 품고 있는 CA의 공개키를 신뢰한다.</li>
<li>그 CA가 서명해서 보증해 준 웹사이트(Alice)의 인증서를 신뢰한다.</li>
<li>고로, 지금 내가 통신하고 있는 이 대상은 해커가 아니라 진짜 웹사이트임을 신뢰한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[KDC (Key Distribution Center)]]></title>
            <link>https://velog.io/@houston_guy2/Key-Establishment</link>
            <guid>https://velog.io/@houston_guy2/Key-Establishment</guid>
            <pubDate>Mon, 20 Apr 2026 18:44:38 GMT</pubDate>
            <description><![CDATA[<h1 id="key-establishment-키-확립-방식">Key Establishment (키 확립 방식)</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c16bdd49-f97d-4369-a719-1b7c38d44744/image.png" alt="">
안전한 통신을 위해 양 종단 간에 비밀키를 설정하는 방법은 크게 두 가지로 나뉩니다.</p>
<ul>
<li>Key Transport (키 전송): 통신하는 두 주체 중 한쪽(One party)이 비밀키를 생성한 후, 이를 암호화하여 상대방에게 전달하는 방식입니다.</li>
<li>Key Agreement (키 합의): 어느 한쪽이 일방적으로 키를 정하는 것이 아니라, 양쪽(Both parties)이 수학적 연산을 통해 공동으로 키를 도출해내는 방식입니다.<ul>
<li>필기 하단에 예시로 적힌 <strong>D-H Key Exchange (디피-헬만 키 교환)</strong>가 가장 대표적입니다.</li>
<li>수식: $K_{AB} = \alpha^{ab} \bmod P$ (양측의 개인값 $a, b$와 공개 변수 $\alpha, P$를 혼합하여 동일한 대칭키 $K_{AB}$를 만들어냅니다.)</li>
</ul>
</li>
</ul>
<h2 id="naive-approach-the-n2-key-distribution-problem">Naive Approach: The $n^2$ Key Distribution Problem</h2>
<p>필기 하단의 네트워크 다이어그램은 대칭키를 가장 단순한 방식(Naive approach)으로 분배할 때 발생하는 치명적인 한계를 보여줍니다.</p>
<p>기본 가정 (Assumption)
네트워크 내의 모든 사용자가 1:1 통신을 위해 각각 고유한 비밀키(Pairwise secret keys)를 나눠 가져야 한다고 가정합니다.</p>
<p>$n=4$ 일 때의 상황 (첫 번째 이미지)Alice, Bob, Chris, David 4명이 통신망에 있습니다.</p>
<ul>
<li>Alice는 3개의 키가 필요합니다. ($K_{AB}, K_{AC}, K_{AD}$)</li>
<li>Bob도 3개의 키가 필요합니다. ($K_{AB}, K_{BC}, K_{BD}$)</li>
<li>결과적으로 4명으로 구성된 전체 네트워크에는 총 6개의 고유한 대칭키가 필요합니다.</li>
</ul>
<p>Frank의 등장: 새로운 사용자 &#39;Frank&#39;가 네트워크에 추가되어 $n=5$가 되었습니다. 빨간색 글씨로 표시된 것처럼, Frank 단 한 명이 추가되었을 뿐인데 기존 4명과 연결하기 위한 4개의 새로운 키($K_{AF}, K_{BF}, K_{CF}, K_{DF}$)가 즉각적으로 필요해집니다.</p>
<p>UH 65000+의 의미: 중앙에 적힌 &#39;T.A.(Trusted Authority)&#39; 아래의 &quot;UH 65000+&quot;는 휴스턴 대학교(University of Houston)의 학생 수 등을 예로 든 것입니다. 만약 65,000명의 사용자가 이 단순한 1:1 방식으로 대칭키를 관리한다면, 시스템 전체에 필요한 키의 개수는 $\frac{n(n-1)}{2}$ 공식에 의해 약 21억 개에 달하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/43952b5f-16da-4f86-9c62-d90f7dae0dd8/image.png" alt=""></p>
<h1 id="kdc-구조의-도입과-한계">KDC 구조의 도입과 한계</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/30fc410d-8c6c-4efa-8b76-c7929e471983/image.png" alt=""></p>
<p>기존에 모든 사용자가 1:1로 직접 키를 교환해야 했던 복잡도 $O(n^2)$의 문제를 해결하기 위해 <strong>KDC(Key Distribution Center)</strong>라는 신뢰할 수 있는 중앙 서버를 도입합니다.</p>
<p>KDC의 작동 원리와 장점</p>
<ul>
<li>선형적 확장성: 통신망에 참여하는 사용자 수 $n$명에 대해 전체 네트워크가 관리해야 할 키의 개수가 $n$개로 크게 줄어듭니다.</li>
<li>KEK (Key Encryption Key): 각 사용자(Alice, Bob 등)는 오직 KDC와 통신하기 위한 마스터키($K_A, K_B$)만 안전하게 나눠 가집니다. 이를 KEK라고 부릅니다.</li>
<li>새로운 사용자(Chris)가 추가될 때, 다른 모든 사용자와 키를 맞출 필요 없이 KDC와 단 한 번만 $K_C$를 초기 설정(Initialization)하면 되므로 인프라 확장이 매우 쉽습니다.</li>
</ul>
<h2 id="현실적인-시스템-취약점-weaknesses">현실적인 시스템 취약점 (Weaknesses)</h2>
<p>효율성이 높아진 대신 보안 구조상 두 가지 심각한 리스크가 발생합니다.</p>
<ul>
<li><p>SPOF (Single Point of Failure, 단일 장애점): KDC 서버가 다운되거나 해킹당하면 네트워크 전체의 암호화 통신이 완전히 마비됩니다.</p>
</li>
<li><p>완전 전방향 무결성(Perfect Forward Secrecy, PFS) 부재: 만약 해커가 특정 사용자의 KEK를 탈취하는 데 성공한다면, 현재의 통신뿐만 아니라 과거에 저장해 두었던 모든 통신 기록까지 소급하여 해독해 낼 수 있습니다. 과거의 세션 키들이 모두 이 KEK로 암호화되어 분배되었기 때문입니다.</p>
</li>
<li><p>참고: 이 KDC 구조는 오늘날 윈도우 네트워크 등에서 널리 쓰이는 인증 프로토콜인 <strong>Kerberos(커베로스)</strong>의 근간이 됩니다.</p>
</li>
</ul>
<h1 id="kdc-취약점을-노린-공격-시나리오">KDC 취약점을 노린 공격 시나리오</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/efd1b155-202f-4810-8b02-002c5497833e/image.png" alt="">
노트는 KDC를 매개로 통신할 때 발생할 수 있는 구체적인 공격 기법 두 가지를 보여줍니다. (필기의 &#39;Reply Attack&#39;은 &#39;Replay Attack&#39;의 오기로 보입니다. 학술적 표준 명칭은 Replay Attack입니다.)</p>
<h2 id="21-replay-attack-재전송-공격">2.1. Replay Attack (재전송 공격)</h2>
<p>전제: 능동형 공격자(Active Attacker)인 Trudy가 과거 통신에서 사용되었던 낡은 세션 키(Old session key)를 어떠한 방법으로 알아낸 상태입니다.</p>
<p>이 과거의 키와 암호문을 통신망에 다시 주입하여, 마치 현재 유효한 통신인 것처럼 시스템을 속이는 공격입니다.</p>
<h2 id="22-key-confirmation-attack-키-확인-공격--중간자-공격">2.2. Key Confirmation Attack (키 확인 공격 / 중간자 공격)</h2>
<p>이 공격은 KDC의 메시지 응답 구조에 &#39;상호 인증&#39;이 빠져 있을 때 발생하는 치명적인 논리적 결함을 파고듭니다. 릴레이 경주에서 주자가 눈을 가린 채 뛰고 있을 때, 누군가 몰래 트랙의 목적지를 바꿔버리는 상황과 같습니다.</p>
<p>[공격의 단계별 흐름]</p>
<ol>
<li><p>가로채기와 조작: Alice는 Bob과 통신하기 위해 KDC에 RQST(ID_A, ID_B)를 보냅니다. 하지만 Trudy가 이를 중간에서 가로채어 목적지를 자신으로 바꾼 RQST(ID_A, ID_T)로 위조하여 KDC에 보냅니다.</p>
</li>
<li><p>KDC의 착각: KDC는 Alice가 Trudy와 통신하길 원한다고 생각하고, 두 사람이 쓸 일회용 세션 키 $K_{ses}$를 생성합니다. 그리고 이를 각각의 KEK로 암호화하여 $Y_A = e_{K_A}(K_{ses})$ 와 $Y_T = e_{K_T}(K_{ses})$ 를 만들어 Trudy에게 보냅니다.</p>
</li>
<li><p>Alice의 착각 (맹점): Trudy는 KDC가 준 $Y_A, Y_T$를 Alice에게 넘깁니다. Alice는 자신의 키 $K_A$로 $Y_A$를 해독하여 $K_{ses}$를 얻습니다. 이때 Alice는 자신이 처음에 Bob을 불렀으므로, 이 세션 키가 당연히 Bob과 공유하는 트랙이라고 굳게 믿습니다. (KDC의 응답에 &quot;이 키는 Trudy용이다&quot;라는 식별표가 없었기 때문입니다.)</p>
</li>
<li><p>해독 완료: Alice는 기밀 데이터 $X$를 방금 얻은 세션 키로 암호화하여 $Y = e_{K_{ses}}(X)$를 만들고, $Y, Y_T$를 전송합니다. 기다리고 있던 Trudy는 자신의 키 $K_T$로 $Y_T$를 열어 $K_{ses}$를 확보한 뒤, Alice의 메시지 $Y$를 완벽하게 해독해 냅니다.</p>
</li>
</ol>
<h1 id="커베로스kerberos-프로토콜의-단순화-버전">커베로스(Kerberos) 프로토콜의 단순화 버전</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/0d89a40c-c2d7-45b3-bcb7-7940c4147900/image.png" alt=""></p>
<h2 id="1-통신-요청-alice-rightarrow-kdc">1. 통신 요청 (Alice $\rightarrow$ KDC)</h2>
<p>$$RQST(ID_A, ID_B, r_A)$$</p>
<ul>
<li>Alice는 KDC에게 &quot;나($ID_A$) Bob($ID_B$)이랑 통신하고 싶어&quot;라고 요청합니다.</li>
<li>핵심 방어 무기 1 - 난수($r_A$): 여기서 Alice는 $r_A$라는 일회용 난수(Nonce)를 생성해서 함께 보냅니다. 이는 나중에 KDC의 응답이 &#39;과거의 응답을 재전송한 것&#39;이 아니라 &#39;지금 막 만들어진 신선한 응답&#39;임을 증명하는 용도로 쓰입니다.</li>
</ul>
<h2 id="2-kdc의-응답-및-티켓-발급-kdc-rightarrow-alice">2. KDC의 응답 및 티켓 발급 (KDC $\rightarrow$ Alice)</h2>
<p>KDC는 세션 키($K_{ses}$)와 유효기간($T$, Lifetime)을 생성한 뒤, 두 개의 암호문($Y_A, Y_B$)을 만들어 Alice에게 보냅니다.</p>
<p>Alice용 암호문 ($Y_A$): $e_{K_A}(K_{ses}, r_A, T, ID_B)$</p>
<ul>
<li>Alice의 마스터키($K_A$)로 암호화되어 있습니다.</li>
</ul>
<p>Bob용 티켓 ($Y_B$): $e_{K_B}(K_{ses}, T, ID_A)$</p>
<ul>
<li>Bob의 마스터키($K_B$)로 암호화되어 있습니다. KDC가 Bob에게 직접 주지 않고 Alice에게 들려 보냅니다. 이를 <strong>티켓(Ticket)</strong>이라고 부릅니다.</li>
</ul>
<h2 id="3-alice의-검증-약점-완벽-방어">3. Alice의 검증 (약점 완벽 방어)</h2>
<p>Alice는 자신의 키 $K_A$로 $Y_A$를 해독하여 내부의 값들을 철저히 검증합니다. 여기서 이전의 공격들이 모두 막힙니다.</p>
<ol>
<li><p>$r_A = r_A&#39;$ 확인 (KDC Authentication): 자신이 보냈던 난수 $r_A$가 응답에 그대로 들어있는지 확인합니다. 맞다면 이 응답은 누군가 가로채서 재전송(Replay)한 것이 아니라, KDC가 지금 막 응답한 것임을 확신할 수 있습니다.</p>
</li>
<li><p>$ID_B$ 확인 (Key Confirmation Attack 방어): 필기의 초록색 박스 부분입니다. KDC가 준 암호문 안에 내가 통신하고자 했던 상대방의 이름($ID_B$)이 명시되어 있습니다. 따라서 해커(Trudy)가 중간에서 목적지를 조작하더라도, Alice는 &quot;어? 난 Bob이랑 통신한다고 했는데 왜 증명서엔 Trudy라고 적혀있지?&quot; 하고 즉시 공격을 알아채고 통신을 끊을 수 있습니다.</p>
</li>
<li><p>$T$ (Lifetime) 확인: 발급받은 키의 유효기간이 지나지 않았는지 확인합니다.</p>
</li>
</ol>
<h2 id="4-alice의-증명서-생성-및-전송-alice-rightarrow-bob">4. Alice의 증명서 생성 및 전송 (Alice $\rightarrow$ Bob)</h2>
<p>검증을 마친 Alice는 Bob에게 통신을 시도합니다. 이때 KDC에게 받았던 Bob용 티켓($Y_B$)과 함께, 자신이 진짜 Alice임을 증명하는 <strong>Authenticator(인증자, $Y_{AB}$)</strong>를 직접 만들어 보냅니다.</p>
<ul>
<li>핵심 방어 무기 2 - 타임스탬프($T_S$): $Y_{AB} = e_{K_{ses}}(ID_A, T_S)$</li>
<li>자신이 Alice라는 정보와 현재 시간($T_S$)을 세션 키로 암호화합니다. (필기 우측 상단에 &quot;Rough syn Alice &amp; Bob&quot;이라고 적힌 이유는 타임스탬프를 확인하려면 양측의 시계가 대략적으로 동기화되어 있어야 하기 때문입니다.)</li>
</ul>
<h2 id="5-bob의-최종-검증">5. Bob의 최종 검증</h2>
<p>메시지를 받은 Bob은 두 가지를 해독합니다.</p>
<ol>
<li><p>KDC가 만든 티켓($Y_B$)을 자신의 마스터키($K_B$)로 열어 <strong>세션 키($K_{ses}$)</strong>와 통신 상대방이 <strong>Alice($ID_A$)</strong>임을 확인합니다.</p>
</li>
<li><p>방금 얻은 세션 키($K_{ses}$)로 Alice가 보낸 Authenticator($Y_{AB}$)를 열어봅니다.</p>
</li>
</ol>
<ul>
<li>안에 적힌 $ID_A&#39;$가 티켓에 적힌 $ID_A$와 일치하는지 확인합니다. (진짜 Alice가 보낸 것이 맞음)</li>
<li>타임스탬프 $T_S$를 확인하여, 이 메시지가 옛날에 해커가 훔쳐둔 메시지를 재전송(Replay)한 것이 아니라 지금 막 보낸 신선한 메시지인지 확인합니다.</li>
</ul>
<p>모든 검증이 끝나면 비로소 세션 키($K_{ses}$)를 이용해 안전하게 실제 데이터 $X$를 암호화($Y = e_{K_{ses}}(X)$)하여 통신을 시작합니다.</p>
]]></description>
        </item>
    </channel>
</rss>