diff --git a/rtl/library/drv8353r_driver.v b/rtl/library/drv8353r_driver.v index f3dfb55..b8d1048 100644 --- a/rtl/library/drv8353r_driver.v +++ b/rtl/library/drv8353r_driver.v @@ -17,7 +17,7 @@ module drv8353r_driver( output drv_cs_n, output drv_sck, output drv_sdi, - output drv_sdo, + input drv_sdo, // assert stop with the coast_nbrake // bit to disable the gate drivers @@ -28,9 +28,11 @@ module drv8353r_driver( output rdy, output fault_a, output fault_b, - output fault_c, + output fault_c ); +parameter WAIT_TIME = 98; + wire [15:0] driver_control; wire [15:0] hs_control; wire [15:0] ls_control; @@ -49,8 +51,9 @@ assign driver_control = { 1'b0, // 1PWM_DIR, default 1'b0, // COAST, disabled 1'b0, // BRAKE, disabled - 1'b0, // CLR_FLT, no clear + 1'b0 // CLR_FLT, no clear }; + // TODO actually calculate HS/LS gate drive // requirements assign hs_control = { @@ -58,7 +61,7 @@ assign hs_control = { 1'b1, // write 3'b000, // LOCK, don't lock or unlock 4'b0100, // IDRIVEP_HS, 300 mA - 4'b0010, // IDRIVEN_HS, 200 mA + 4'b0010 // IDRIVEN_HS, 200 mA }; assign ls_control = { 4'b0100, // address = 0x04 @@ -66,7 +69,7 @@ assign ls_control = { 1'b1, // CBC, OCP conditions reset when PWM is provided 2'b11, // TDRIVE = 4000 ns gate-current drive time 4'b0100, // IDRIVEP_LS, 300 mA - 4'b0010, // IDRIVEN_LS, 200 mA + 4'b0010 // IDRIVEN_LS, 200 mA }; assign ocp_control = { 4'b0101, // address = 0x05 @@ -75,7 +78,7 @@ assign ocp_control = { 2'b01, // DEAD_TIME, 100 ns deadtime 2'b01, // OCP_MODE, overcurrent causes automatic retry 2'b10, // OCP_DEG, 4us overcurrent deglitch - 4'b1101, // VDS_LVL, 1V + 4'b1101 // VDS_LVL, 1V }; assign csa_control = { 4'b0110, // address = 0x06 @@ -88,12 +91,11 @@ assign csa_control = { 1'b0, // CSA_CAL_A, normal operation 1'b0, // CSA_CAL_B, normal operation 1'b0, // CSA_CAL_C, normal operation - 2'b11, // SEN_LVL, OCP 1 V + 2'b11 // SEN_LVL, OCP 1 V }; -reg [3:0] cur_bit; -wire [4:0] config_bits; -wire config_bit; +wire [3:0] cur_bit; +wire [7:0] config_bits; assign config_bits[0] = driver_control[cur_bit]; assign config_bits[1] = hs_control[cur_bit]; @@ -102,5 +104,141 @@ assign config_bits[3] = ocp_control[cur_bit]; assign config_bits[4] = csa_control[cur_bit]; +assign config_bits[7:5] = 3'b000; + +localparam IDLE = 0, + INIT = 1, + CONFIG = 2, + READY = 3; +reg [2:0] state = IDLE; +reg [2:0] state_next; + +assign rdy = (state == READY); + +reg drv_en_ff = 0; +reg drv_cs_n_ff = 1; +reg drv_sck_ff = 1; +reg drv_sdi_ff = 0; + +reg drv_en_next, drv_cs_n_next; +reg drv_sck_next, drv_sdi_next; + +assign drv_en = drv_en_ff; +assign drv_cs_n = drv_cs_n_ff; +assign drv_sck = drv_sck_ff; +assign drv_sdi = drv_sdi_ff; + +reg [7:0] count = 0; +reg [7:0] count_next; +reg [7:0] msb_count = 0; +reg [7:0] msb_count_next; + +assign cur_bit = 15 - count[6:3]; + +reg [2:0] cur_reg = 0; +reg [2:0] cur_reg_next; + +always @* begin + state_next = state; + + drv_en_next = 0; + drv_cs_n_next = 1; + drv_sck_next = 0; + drv_sdi_next = 0; + cur_reg_next = 0; + + count_next = count; + msb_count_next = msb_count; + + case (state) + IDLE: begin + if (en) begin + state_next = INIT; + drv_en_next = 1; + count_next = 0; + end + end + INIT: begin + drv_en_next = 1; + + // wait 1 ms before trying to configure + count_next = count + 1; + if (&count_next) begin + msb_count_next = msb_count; + end + if (msb_count == WAIT_TIME) begin + cur_reg_next = 0; + state_next = CONFIG; + count_next = 0; + end + end + CONFIG: begin + // write all the configuration bits + // to the DRV8353 + + drv_en_next = 1; + drv_cs_n_next = drv_cs_n_ff; + drv_sck_next = drv_sck_ff; + drv_sdi_next = drv_sdi_ff; + cur_reg_next = cur_reg; + + count_next = count + 1; + if (&count[1:0]) begin + if (count[7:2] == 0) begin + drv_cs_n_next = 0; + end + drv_sck_next = count[2]; + if (count[7:2] == 33) begin + drv_cs_n_next = 1; + count_next = 0; + cur_reg_next = cur_reg + 1; + drv_sck_next = 0; + if (cur_reg == 4) begin + state_next = READY; + end + end + // write the next bit on high transitions + if (count[2]) begin + drv_sdi_next = config_bits[cur_reg]; + end + end + end + READY: begin + drv_en_next = 1; + // TODO if there are faults read + // the status register to output + // the appropriate info + // TODO if a stop is commanded + // write to the register + // to stop the motor driver + // TODO if a stop is deasserted + // write to the register to + // reenable the motor driver + end + endcase + + if (rst) begin + state_next = IDLE; + + drv_en_next = 0; + drv_cs_n_next = 1; + drv_sck_next = 1; + drv_sdi_next = 0; + end +end + +always @(posedge clk) begin + state <= state_next; + + drv_en_ff <= drv_en_next; + drv_cs_n_ff <= drv_cs_n_next; + drv_sck_ff <= drv_sck_next; + drv_sdi_ff <= drv_sdi_next; + + count <= count_next; + msb_count <= msb_count_next; + + cur_reg <= cur_reg_next; +end endmodule diff --git a/rtl/tb/Makefile b/rtl/tb/Makefile index 9670a96..668fd1b 100644 --- a/rtl/tb/Makefile +++ b/rtl/tb/Makefile @@ -1,4 +1,4 @@ -tests: test_adc +tests: test_adc test_drv8353 test_adc: vcd/adc_driver_tb.vcd @@ -7,3 +7,11 @@ vcd/adc_driver_tb.vcd: bin/adc_driver_tb bin/adc_driver_tb: adc_driver_tb.v ../library/adc_driver.v iverilog adc_driver_tb.v ../library/adc_driver.v -o bin/adc_driver_tb + +test_drv8353: vcd/drv8353_spi_tb.vcd + +vcd/drv8353_spi_tb.vcd: bin/drv8353_spi_tb + ./bin/drv8353_spi_tb + +bin/drv8353_spi_tb: drv8353_spi_tb.v ../library/drv8353r_driver.v + iverilog $^ -o bin/drv8353_spi_tb diff --git a/rtl/tb/drv8353_spi_tb.v b/rtl/tb/drv8353_spi_tb.v new file mode 100644 index 0000000..fe9487c --- /dev/null +++ b/rtl/tb/drv8353_spi_tb.v @@ -0,0 +1,137 @@ +`timescale 10ns/10ns + +module drv8353_spi_tb(); + +reg clk = 0; +reg rst = 1; +reg en = 0; + +reg drv_fault_n = 1; +wire drv_en; + +wire drv_cs_n; +wire drv_sck; +wire drv_sdi; +reg drv_sdo = 0; + +// assert stop with the coast_nbrake +// bit to disable the gate drivers +reg stop = 0; +reg coast_nbrake = 0; +reg clear_fault = 0; + +wire rdy; +wire fault_a; +wire fault_b; +wire fault_c; + +drv8353r_driver #(0) dut( + .clk(clk), + .rst(rst), + .en(en), + + .drv_fault_n(drv_fault_n), + .drv_en(drv_en), + + .drv_cs_n(drv_cs_n), + .drv_sck(drv_sck), + .drv_sdi(drv_sdi), + .drv_sdo(drv_sdo), + + .stop(stop), + .coast_nbrake(coast_nbrake), + .clear_fault(clear_fault), + + .rdy(rdy), + .fault_a(fault_a), + .fault_b(fault_b), + .fault_c(fault_c) +); + +integer cfg_num = -1; +integer cfg[5:0]; +integer i = 0; +integer bit_count = 0; +reg spi_active = 0; + +wire [15:0] cfg0; +wire [15:0] cfg1; +wire [15:0] cfg2; +wire [15:0] cfg3; +wire [15:0] cfg4; + +assign cfg0 = cfg[0]; +assign cfg1 = cfg[1]; +assign cfg2 = cfg[2]; +assign cfg3 = cfg[3]; +assign cfg4 = cfg[4]; + +always @(negedge drv_cs_n) begin + spi_active <= 1; + cfg_num <= cfg_num + 1; + if (drv_sck) begin + $display("sck not low on falling edge, clock %d", i); + end +end + +always @(posedge drv_cs_n) begin + spi_active <= 0; + bit_count <= 0; + if (drv_sck) begin + $display("sck not low on rising edge, clock %d", i); + end +end + +always @(negedge drv_sck) begin + if (spi_active) begin + cfg[cfg_num] <= cfg[cfg_num] << 1 | drv_sdi; + bit_count <= bit_count + 1; + end +end + +integer j = 0; +integer expected = 0; +always @* + case (j) + 0: expected = 16'b0010100000100000; + 1: expected = 16'b0011100001000010; + 2: expected = 16'b0100111101000010; + 3: expected = 16'b0101100101101101; + 4: expected = 16'b0110100010000011; + endcase + + +always @(rdy) begin + if (rdy) begin + for (j = 0; j < 5; j += 1) + #1 + if (cfg[j] != expected) + $display("cfg %d bad, %x != %x", j, cfg[j], expected); + $finish; + end +end + +initial begin + $dumpfile("vcd/drv8353_spi_tb.vcd"); + $dumpvars; + + for (i = 0; i < 5; i += 1) cfg[i] = 0; + // TODO cycle the module long enough to get all the configuration bits + // written out, and check to see if it worked correctly + i = 0; + + #2 clk = 0; + #2 clk = 1; + + i = i + 1; + rst = 0; + en = 1; + + for (i = 0; i < 3000; i+= 1) begin + #2 clk = 0; + #2 clk = 1; + i = i + 1; + end +end + +endmodule