{"task_id":"spi_controller","description":"SPI Controller with multiple state machine, clock divider, and protocol handling","header":"module spi_controller (\n input clk,\n input rst_n,\n input [7:0] data_in,\n input start,\n input cpol,\n input cpha,\n output reg [7:0] data_out,\n output reg busy,\n output reg spi_clk,\n output reg mosi,\n input miso\n);","module_code":"module spi_controller (\n input clk,\n input rst_n,\n input [7:0] data_in,\n input start,\n input cpol,\n input cpha,\n output reg [7:0] data_out,\n output reg busy,\n output reg spi_clk,\n output reg mosi,\n input miso\n);\n localparam IDLE = 4'd0, CLK_LOW = 4'd1, CLK_HIGH = 4'd2, SAMPLE = 4'd3, SHIFT = 4'd4, STOP = 4'd5, ERROR = 4'd6, PREPARE = 4'd7, TURNAROUND = 4'd8, DUMMY = 4'd9;\n localparam DIV_2 = 3'd0, DIV_4 = 3'd1, DIV_8 = 3'd2, DIV_16 = 3'd3;\n reg [3:0] state, next_state;\n reg [3:0] bit_count;\n reg [2:0] clk_div;\n reg [2:0] div_counter;\n reg internal_clk;\n reg [7:0] shift_reg;\n reg error_flag;\n reg mode_fault;\n\n always @(posedge clk or negedge rst_n) begin\n if (!rst_n) begin\n internal_clk <= 1'b0;\n div_counter <= 3'd0;\n end else if (state == IDLE) begin\n internal_clk <= 1'b0;\n div_counter <= 3'd0;\n end else begin\n if (div_counter >= clk_div) begin\n div_counter <= 3'd0;\n internal_clk <= ~internal_clk;\n end else begin\n div_counter <= div_counter + 1'b1;\n end\n end\n end\n\n always @(*) begin\n next_state = state;\n case (state)\n IDLE: if (start && !busy) next_state = PREPARE;\n PREPARE: next_state = TURNAROUND;\n TURNAROUND: next_state = CLK_LOW;\n CLK_LOW: next_state = (!cpha) ? SAMPLE : SHIFT;\n CLK_HIGH: begin\n if (cpha) next_state = SAMPLE;\n else if (bit_count == 4'd0) next_state = STOP;\n else next_state = CLK_LOW;\n end\n SAMPLE: next_state = (bit_count == 4'd0) ? CLK_HIGH : SHIFT;\n SHIFT: next_state = CLK_HIGH;\n STOP: next_state = DUMMY;\n DUMMY: next_state = IDLE;\n ERROR: next_state = (mode_fault) ? ERROR : IDLE;\n default: next_state = IDLE;\n endcase\n end\n\n always @(posedge clk or negedge rst_n) begin\n if (!rst_n) begin\n state <= IDLE;\n bit_count <= 4'd7;\n shift_reg <= 8'd0;\n data_out <= 8'd0;\n busy <= 1'b0;\n mosi <= 1'b0;\n error_flag <= 1'b0;\n mode_fault <= 1'b0;\n clk_div <= DIV_4;\n end else begin\n state <= next_state;\n case (state)\n IDLE: begin\n busy <= 1'b0;\n bit_count <= 4'd7;\n if (start && !busy) begin busy <= 1'b1; shift_reg <= data_in; end\n end\n PREPARE: begin busy <= 1'b1; mosi <= shift_reg[7]; end\n TURNAROUND: mosi <= 1'b0;\n CLK_LOW: if (cpha == 1'b0) mosi <= shift_reg[7];\n SAMPLE: begin\n if (cpha == 1'b1) mosi <= shift_reg[7];\n shift_reg <= {shift_reg[6:0], miso};\n end\n SHIFT: begin\n shift_reg <= {shift_reg[6:0], miso};\n if (bit_count > 4'd0) bit_count <= bit_count - 1'b1;\n end\n CLK_HIGH: begin\n if (cpha == 1'b0) begin\n shift_reg <= {shift_reg[6:0], miso};\n if (bit_count > 4'd0) bit_count <= bit_count - 1'b1;\n end\n if (error_flag) mode_fault <= 1'b1;\n end\n STOP: begin data_out <= shift_reg; busy <= 1'b0; mosi <= 1'b0; end\n DUMMY: busy <= 1'b0;\n ERROR: busy <= 1'b1;\n endcase\n end\n end\nendmodule","testbench":"`timescale 1ns / 1ps\nmodule tb_spi_controller;\n reg clk, rst_n, start, cpol, cpha, miso;\n reg [7:0] data_in;\n wire [7:0] data_out;\n wire busy, spi_clk, mosi;\n spi_controller dut (.clk(clk), .rst_n(rst_n), .data_in(data_in), .start(start), .cpol(cpol), .cpha(cpha), .data_out(data_out), .busy(busy), .spi_clk(spi_clk), .mosi(mosi), .miso(miso));\n always #5 clk = ~clk;\n initial begin clk=0; rst_n=0; data_in=8'h00; start=0; cpol=0; cpha=0; miso=0; #20 rst_n=1; #10;\n @(posedge clk); data_in=8'hA5; start=1; @(posedge clk); start=0;\n wait(busy==0); #50;\n @(posedge clk); data_in=8'h5A; start=1; cpol=1; cpha=1; @(posedge clk); start=0;\n wait(busy==0); #50;\n @(posedge clk); data_in=8'hFF; start=1; cpol=0; cpha=1; @(posedge clk); start=0;\n wait(busy==0); #100; $finish;\n end\nendmodule"}