Electronic Engeneering/Hardware Description Language

Verilog 자판기 설계 (code, tb, RTL 합성회로, 파형)

굠민 2024. 12. 12. 23:59

시간 기반 할인과 잔액 반환을 지원하는 스마트 자판기 

 

📍회로 기능 설명

이 자판기 회로는 FSM(유한 상태 기계)을 기반으로 동전 투입, 상품 선택 및 배출, 잔액 반환 기능을 구현한 시스템입니다. 기본적으로 IDLE(대기) 상태에서 동전이 투입되면 잔액이 갱신되며, 사용자가 상품을 선택하면 DISPENSE(상품 배출) 상태로 전환되어 상품을 배출하고 잔액을 차감합니다. 이때, 1/8 분주기(Clock Divider)를 활용하여 일정 간격으로 할인 기능이 활성화되며, 할인 조건이 활성화된 경우 상품을 50% 할인된 가격으로 구매할 수 있습니다. 반환 버튼이 눌릴 경우 RETURN(잔액 반환) 상태로 전환되어 남은 잔액만큼 동전을 반환하며, 스텝 모터 제 어를 통해 동전 반환을 정밀하게 수행합니다. 반환이 완료되면 모터를 비활성화하고 다시 IDLE 상태로 전환됩니다. 이러한 구조는 상태 전이를 통해 자판기의 주요 기능을 안정적으로 수행하며, 사용자 편의성을 높이는 할인 및 잔액 반환 기능을 제공합니다.

 

 

📍상태 천이도

 

 

 

📄모듈 코드(Module Code)

module vending_machine (
    input clk,               // 클럭 신호
    input rst,               // 리셋 신호
    input coin,              // 동전 투입 신호
    input refund,            // 잔액 반환 신호
    input [1:0] select,      // 음료 선택 신호
    output reg dispense,     // 음료 배출 신호
    output reg motor_enable, // 모터 활성화 신호
    output reg [3:0] balance, // 잔액 표시
    output reg [3:0] motor_step,  // 모터 단계 제어 신호
    output reg discount_active // 분주된 클럭(할인 활성화 상태 표시)
 );
    // 상태 정의 (FSM)
    parameter IDLE        = 3'b000;
    parameter DISPENSE    = 3'b100;
    parameter RETURN      = 3'b101;
    reg [2:0] state_reg, next_state; // 현재 상태 및 다음 상태
    reg [3:0] normal_price;          // 음료의 정가
    reg [3:0] refund_counter;        // 반환할 동전 수 카운터
    (* KEEP = "TRUE" *) reg [2:0] div_counter; // 1/8 분주기 카운터 + 최적화 방지 코드
<코드>
    reg [3:0] discount_price;        // 할인된 음료 가격 
 
 // 할인 조건 활성화를 위한 1/8 분주기 생성 (duty cycle 50%)
 always @(posedge clk or posedge rst) begin
 if (rst) begin
 div_counter <= 3'b000;
 discount_active <= 1'b0;
 end
 else begin
 if(div_counter == 3'b011) begin
 div_counter <= 3'b000;
 discount_active <= ~ discount_active;
 end
 else div_counter <= div_counter +1;
 end
 end
    // 음료 가격 설정 : 할인 조건이 활성화되면 50%할인이 적용된다.
    always @(*) begin
        case (select)
            2'b01: begin
                normal_price = 4;
                discount_price = 2;
            end
            2'b10: begin
                normal_price = 6;
                discount_price = 3;
            end
            2'b11: begin
                normal_price = 8;
                discount_price = 4;
            end
            default: begin
                normal_price = 0;
                discount_price = 0;
            end
        endcase
    end
    // 상태 전이 로직
    always @(posedge clk or posedge rst) begin
        if (rst)
            state_reg <= IDLE; // 리셋 시 초기 상태로 이동
        else
            state_reg <= next_state; // 다음 상태로 전환
    end
    // 다음 상태 결정 (FSM)
    always @(*) begin
        next_state = state_reg; // 기본값: 현재 상태 유지
        case (state_reg)
            IDLE: begin
                if (coin) 
next_state = IDLE; // 동전 투입 시 잔액 갱신
 else if (select!=2'b00 && balance >= 
(discount_active ? discount_price : normal_price))
                  next_state = DISPENSE; // 음료 선택 및 잔액 확인 후 DISPENSE로 전환
                else if (refund)
                  next_state = RETURN; // 반환 버튼 누르면 RETURN 상태로 전환
            end
            DISPENSE: begin
                next_state = IDLE; // DISPENSE 후 IDLE로 이동
            end
            RETURN: begin
                if (balance == 0)
                    next_state = IDLE; // 반환 완료 후 IDLE로 이동
            end
        endcase
    end
 // 상태별 동작 정의
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            dispense <= 0;
            motor_enable <= 0;
            balance <= 0;
            refund_counter <= 0;
            motor_step <= 4'b0000;
        end 
  else begin
            case (state_reg)
                IDLE: begin
                    dispense <= 0;
                    motor_enable <= 0;
                    if (coin)
                        balance <= balance + 1; // 동전 투입 시 잔액 갱신
                end
                DISPENSE: begin
                    dispense <= 1; // 음료 배출
                    if (discount_active)
                        balance <= balance - discount_price; // 할인 가격 차감
                    else
                        balance <= balance - normal_price; // 정가 차감
                end
                RETURN: begin
                    if (refund_counter == 0 && balance >0) begin
                        refund_counter <= balance; // 반환할 동전 초기화
                        motor_enable <= 1; // 모터 활성화
  end
  else if (refund_counter > 0) begin
                        case (refund_counter % 4) // 잔액에 따른 모터 실행
                            0: motor_step <= 4'b1001;
                            1: motor_step <= 4'b1100;
                            2: motor_step <= 4'b0110;
                            3: motor_step <= 4'b0011;
                        endcase
                        refund_counter <= refund_counter - 1;
 balance <= balance -1;
                    end 
  else if(refund_counter==0) begin
                        motor_enable <= 0; // 반환 완료 시 모터 비활성화
                        balance <= 0;
 next_state <= IDLE;        
// 반환 완료 후 IDLE로 전환
                    end
                end
            endcase
        end
    end
 endmodule

 

 

📄테스트 벤치 코드 (Test Bench Code)

 module tb_vending_machine;
 // Inputs
 reg clk;
 reg rst;
 reg coin;
 reg refund;
 reg [1:0] select;
 // Outputs
 wire dispense;
 wire motor_enable;
 wire [3:0] balance;
 wire [3:0] motor_step;
 wire discount_active;
 // Instantiate the Unit Under Test (UUT)
 vending_machine uut (
 .clk(clk), 
.rst(rst), 
.coin(coin), 
.refund(refund), 
.select(select), 
.dispense(dispense), 
.motor_enable(motor_enable), 
.balance(balance), 
.motor_step(motor_step), 
.discount_active(discount_active)
 );
 // Generate clock signal
 always #10 clk = ~clk; // 10ns clock period
 initial begin
 // Initialize Inputs
 clk = 0;
 rst = 1;
 coin = 0;
 refund = 0;
 select = 2'b00;
// Wait for reset
 #10 rst = 0;
 // Step 1: Insert 3 coins
 #10 coin = 1; 
#20 coin = 0; // Insert first coin
 #10 coin = 1; 
#20 coin = 0; // Insert second coin
 #10 coin = 1; 
#20 coin = 0; // Insert third coin
 #10 coin = 1;
 #20 coin = 0; // Insert fourth coin
 // Step 2: Select drink 1 (cost 2 coins in discount)
 select = 2'b01; // Select drink 1
 // Step 4: Refund remaining balance
 #60 refund = 1;
 #30 refund = 0;
 // Finish simulation
 #300 $finish;
 end
 endmodule

 

📄RTL Schematic

 

📄Technology Schematic

 

📄 파형 & 분석

 

 *clk 신호는 10ns간격으로 변동한다.
 *1/8 분주기(Duty Cycle 50%)가 4번의 상승 클록 신호 후 변동한다. → 할인 조건
 
<0ns~10ns: 초기화 상태>
 rst = 1: 리셋 신호가 활성화되어 모든 상태와 레지스터가 초기화된다.
현재 상태(state_reg) : IDLE

이후, rst신호가 비활성화되어 FSM이 동작한다.

 <50ns~150ns: 동전 투입>
동전 신호(coin=1)입력 시, 상승 클럭 타이밍에 따라 잔액(balance)이 증가한다.
마지막 4번째 동전 투입 후 잔액(balance) : 0011

 <130ns>
 select = 2'b01 : 첫 번째 음료가 선택된다.
현재 상태(state_reg) : DISPENSE

 <150ns>
 dispense = 1 : 할인된 첫 번째 음료의 가격(2)이 잔액(4)에서 차감된다.
잔액 : 2(0010)

 <190ns>
 refund=1 : 반환 신호가 활성화된다. 
현재 상태 : RETURN으로 전환된다.
잔돈을 반환해주는 모터가 활성화되며 잔액이 감소한다.

 <260ns> 
잔액이 0이 됨에 따라 반환 모터가 비활성화된다