Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added motor controller interfaces

digital I/O, ADC, and motor PWM/servo PWM interfaces added.
Also, register bank expanded.
  • Loading branch information...
commit 8e75c74c8fe63fe2eb63ac6a60eb99a69c6b29fa 1 parent 613fbc9
@bunnie authored
View
125 kovan1.srcs/sources_1/imports/kovan1/common/i2c_slave.v
@@ -127,12 +127,60 @@ module i2c_slave (
input wire [7:0] reg_3b,
input wire [7:0] reg_3c,
input wire [7:0] reg_3d,
-
input wire [7:0] reg_3e,
+
input wire [7:0] reg_3f,
output wire [7:0] reg_40,
+ output wire [7:0] reg_41,
+ output wire [7:0] reg_42,
+ output wire [7:0] reg_43,
+ output wire [7:0] reg_44,
+ output wire [7:0] reg_45,
+ output wire [7:0] reg_46,
+ output wire [7:0] reg_47,
+ output wire [7:0] reg_48,
+ output wire [7:0] reg_49,
+ output wire [7:0] reg_4a,
+ output wire [7:0] reg_4b,
+ output wire [7:0] reg_4c,
+ output wire [7:0] reg_4d,
+ output wire [7:0] reg_4e,
+ output wire [7:0] reg_4f,
+ output wire [7:0] reg_50,
+ output wire [7:0] reg_51,
+ output wire [7:0] reg_52,
+ output wire [7:0] reg_53,
+ output wire [7:0] reg_54,
+ output wire [7:0] reg_55,
+ output wire [7:0] reg_56,
+ output wire [7:0] reg_57,
+ output wire [7:0] reg_58,
+ output wire [7:0] reg_59,
+ output wire [7:0] reg_5a,
+ output wire [7:0] reg_5b,
+
+ output wire [7:0] reg_60,
+ output wire [7:0] reg_61,
+ output wire [7:0] reg_62,
+ output wire [7:0] reg_63,
+ output wire [7:0] reg_64,
+ output wire [7:0] reg_65,
+ output wire [7:0] reg_66,
+ output wire [7:0] reg_67,
+ output wire [7:0] reg_68,
+
input wire [7:0] reg_80,
+ input wire [7:0] reg_81,
+ input wire [7:0] reg_82,
+ input wire [7:0] reg_83,
+
+ input wire [7:0] reg_90,
+ input wire [7:0] reg_91,
+ input wire [7:0] reg_92,
+ input wire [7:0] reg_93,
+ input wire [7:0] reg_94,
+ input wire [7:0] reg_95,
input wire [7:0] reg_fc,
input wire [7:0] reg_fd,
@@ -235,7 +283,7 @@ module i2c_slave (
/// extended register set for Kovan
parameter EXT_RAM_WIDTH = 8;
- parameter EXT_RAM_ADDR_BITS = 5; // note parameter width exception in reg_a* assign block below
+ parameter EXT_RAM_ADDR_BITS = 6; // note parameter width exception in reg_a* assign block below
reg [EXT_RAM_WIDTH-1:0] I2C_regblock_ext [(2**RAM_ADDR_BITS)-1:0];
reg [EXT_RAM_WIDTH-1:0] I2C_regread_async_ext;
@@ -589,9 +637,45 @@ module i2c_slave (
assign reg_1f = I2C_regblock[5'h1f]; // msb of Km
/// extended block
- assign reg_40 = I2C_regblock_ext[5'h0]; // note the renumbering -- it's (target address - 0x40)
+ assign reg_40 = I2C_regblock_ext[6'h0]; // note the renumbering -- it's (target address - 0x40)
+ assign reg_41 = I2C_regblock_ext[6'h1];
+ assign reg_42 = I2C_regblock_ext[6'h2];
+ assign reg_43 = I2C_regblock_ext[6'h3];
+ assign reg_44 = I2C_regblock_ext[6'h4];
+ assign reg_45 = I2C_regblock_ext[6'h5];
+ assign reg_46 = I2C_regblock_ext[6'h6];
+ assign reg_47 = I2C_regblock_ext[6'h7];
+ assign reg_48 = I2C_regblock_ext[6'h8];
+ assign reg_49 = I2C_regblock_ext[6'h9];
+ assign reg_4a = I2C_regblock_ext[6'ha];
+ assign reg_4b = I2C_regblock_ext[6'hb];
+ assign reg_4c = I2C_regblock_ext[6'hc];
+ assign reg_4d = I2C_regblock_ext[6'hd];
+ assign reg_4e = I2C_regblock_ext[6'he];
+ assign reg_4f = I2C_regblock_ext[6'hf];
+ assign reg_50 = I2C_regblock_ext[6'h10];
+ assign reg_51 = I2C_regblock_ext[6'h11];
+ assign reg_52 = I2C_regblock_ext[6'h12];
+ assign reg_53 = I2C_regblock_ext[6'h13];
+ assign reg_54 = I2C_regblock_ext[6'h14];
+ assign reg_55 = I2C_regblock_ext[6'h15];
+ assign reg_56 = I2C_regblock_ext[6'h16];
+ assign reg_57 = I2C_regblock_ext[6'h17];
+ assign reg_58 = I2C_regblock_ext[6'h18];
+ assign reg_59 = I2C_regblock_ext[6'h19];
+ assign reg_5a = I2C_regblock_ext[6'h1a];
+ assign reg_5b = I2C_regblock_ext[6'h1b];
-
+ assign reg_60 = I2C_regblock_ext[6'h20];
+ assign reg_61 = I2C_regblock_ext[6'h21];
+ assign reg_62 = I2C_regblock_ext[6'h22];
+ assign reg_63 = I2C_regblock_ext[6'h23];
+ assign reg_64 = I2C_regblock_ext[6'h24];
+ assign reg_65 = I2C_regblock_ext[6'h25];
+ assign reg_66 = I2C_regblock_ext[6'h26];
+ assign reg_67 = I2C_regblock_ext[6'h27];
+ assign reg_68 = I2C_regblock_ext[6'h28];
+
always @(*) begin
case (I2C_addr[7:0])
8'h2: begin
@@ -717,10 +801,41 @@ module i2c_slave (
I2C_regread_async = reg_3f;
end
+
8'h80: begin
I2C_regread_async = reg_80;
end
-
+ 8'h81: begin
+ I2C_regread_async = reg_81;
+ end
+ 8'h82: begin
+ I2C_regread_async = reg_82;
+ end
+ 8'h83: begin
+ I2C_regread_async = reg_83;
+ end
+
+
+ 8'h90: begin
+ I2C_regread_async = reg_90;
+ end
+ 8'h91: begin
+ I2C_regread_async = reg_91;
+ end
+ 8'h92: begin
+ I2C_regread_async = reg_92;
+ end
+ 8'h93: begin
+ I2C_regread_async = reg_93;
+ end
+ 8'h94: begin
+ I2C_regread_async = reg_94;
+ end
+ 8'h95: begin
+ I2C_regread_async = reg_95;
+ end
+
+
8'hfc: begin
I2C_regread_async = reg_fc;
end
View
39 kovan1.srcs/sources_1/imports/kovan1/common/mot_pwm.v
@@ -0,0 +1,39 @@
+module mot_pwm(
+ input wire clk,
+ input wire [PWM_PRECISION_WIDTH-1:0] duty_cycle,
+ output wire pwm_output
+ );
+
+ parameter PWM_PRECISION_WIDTH = 12;
+
+ reg [PWM_PRECISION_WIDTH-1:0] duty_cycle_reg, temp_reg, old_dc;
+ reg [PWM_PRECISION_WIDTH-1:0] pwm_count;
+
+ reg new_duty_cycle;
+ reg pwm_state;
+ reg got_new_dc;
+
+ // synchronize changes to local register if d/c changes
+ always @ (posedge clk) begin
+ if (~|pwm_count) begin
+ duty_cycle_reg <= duty_cycle; // only update when pwm_count is at 0 state
+ end else begin
+ duty_cycle_reg <= duty_cycle_reg;
+ end
+ end
+
+ // now PWM
+ always @(posedge clk) begin
+ pwm_count <= pwm_count + 1;
+ if( &duty_cycle_reg ) begin
+ pwm_state <= 1'b1; // if duty cycle is 100%, don't glitch
+ end else if( duty_cycle_reg > pwm_count ) begin
+ pwm_state <= 1'b1;
+ end else begin
+ pwm_state <= 1'b0;
+ end
+ end // always @ (posedge clk or posedge reset)
+
+ assign pwm_output = pwm_state;
+
+endmodule // mot_pwm
View
49 kovan1.srcs/sources_1/imports/kovan1/common/servo_pwm.v
@@ -0,0 +1,49 @@
+// servo PWM module
+// basically, you have a major period, which is approx. 20 ms (50Hz)
+// and then a pulse, which has a tmin ~ 1ms duration
+// and a tmax ~ 2ms duration
+// and you want to subdivide the pulse by some number of ticks
+
+// we have a 26 MHz clock available which should theoretically give
+// us 38ns resolution if this is done right.
+
+// let's assume the 26 MHz clock is a given, fixed, and invariant.
+//
+// first, we need to have a mechanism to tell us when 20 ms has passed.
+// when that has passed, we then want to count out a period of time
+// which corresponds to tmin + pwmval * delta,
+// where pwmval * delta < (tmax - tmin)
+
+module servo_pwm(
+ input wire clk, // 26 MHz
+ input wire [23:0] period,
+ input wire [23:0] pulse,
+ output wire pwm_output
+ );
+
+ reg [23:0] period_cnt;
+ reg pwm_state;
+ reg pwm_deglitch;
+
+ always @(posedge clk) begin
+ if( period_cnt[23:0] >= period[23:0] ) begin
+ period_cnt <= 24'h0;
+ end else begin
+ period_cnt <= period_cnt + 1;
+ end
+ end // always @ (posedge clk or posedge reset)
+
+ always @(posedge clk) begin
+ if( period_cnt > pulse ) begin
+ pwm_state <= 1;
+ end else begin
+ pwm_state <= 0;
+ end
+
+ pwm_deglitch <= pwm_state;
+ end // always @ (posedge clk or posedge reset)
+
+ assign pwm_output = !pwm_deglitch;
+
+endmodule // servo_pwm
+
View
275 kovan1.srcs/sources_1/imports/kovan1/kovan.v
@@ -240,7 +240,88 @@ module kovan (
///////////////////////////////////////////
// motor control unit
-
+ wire clk3p2M; // hooked up down below in the cheezy clock divider section
+ wire [7:0] dig_out_val;
+ wire [7:0] dig_oe;
+ wire [7:0] dig_pu;
+ wire [7:0] ana_pu;
+ wire [7:0] dig_in_val;
+ wire dig_val_good;
+ wire dig_busy;
+ wire dig_sample;
+ wire dig_update;
+
+ wire [9:0] adc_in;
+ wire [3:0] adc_chan;
+ wire adc_valid;
+ wire adc_go;
+
+ wire [15:0] mot_pwm_div;
+ wire [15:0] mot_pwm_duty;
+ wire [7:0] mot_drive_code;
+ wire mot_allstop;
+
+ wire [23:0] servo_pwm_period;
+ wire [23:0] servo0_pwm_pulse;
+ wire [23:0] servo1_pwm_pulse;
+ wire [23:0] servo2_pwm_pulse;
+ wire [23:0] servo3_pwm_pulse;
+
+ robot_iface iface(.clk(clk26buf), .glbl_reset(glbl_reset),
+ .clk_3p2MHz(clk3p2M),
+
+ // digital i/o block
+ .dig_out_val(dig_out_val),
+ .dig_oe(dig_oe),
+ .dig_pu(dig_pu),
+ .ana_pu(ana_pu),
+ .dig_in_val(dig_in_val),
+ .dig_val_good(dig_val_good), // output value is valid when high
+ .dig_busy(dig_busy), // chain is busy when high
+ .dig_sample(dig_sample), // samples input on rising edge
+ .dig_update(dig_update), // updates chain on rising edge
+
+ // ADC interface
+ .adc_in(adc_in),
+ .adc_chan(adc_chan), // channels 0-7 are for user, 8-15 are for motor current fbk
+ .adc_valid(adc_valid),
+ .adc_go(adc_go),
+
+ // motor driver interface
+ .mot_pwm_div(mot_pwm_div),
+ .mot_pwm_duty(mot_pwm_duty),
+ .mot_drive_code(mot_drive_code), // 2 bits/chan, 00 = stop, 01 = forward, 10 = rev, 11 = stop
+ .mot_allstop(mot_allstop),
+
+ // servo interface
+ .servo_pwm_period(servo_pwm_period), // total period for the servo update
+ .servo0_pwm_pulse(servo0_pwm_pulse), // pulse width in absolute time
+ .servo1_pwm_pulse(servo1_pwm_pulse),
+ .servo2_pwm_pulse(servo2_pwm_pulse),
+ .servo3_pwm_pulse(servo3_pwm_pulse),
+
+ /////// physical interfaces to outside the chip
+ // motors
+ .MBOT(MBOT[3:0]),
+ .MTOP(MTOP[3:0]),
+ .MOT_EN(MOT_EN),
+ .M_SERVO(M_SERVO[3:0]),
+
+ // analog interface
+ .DIG_ADC_CS(DIG_ADC_CS),
+ .DIG_ADC_IN(DIG_ADC_IN),
+ .DIG_ADC_OUT(DIG_ADC_OUT),
+ .DIG_ADC_SCLK(DIG_ADC_SCLK),
+
+ // digital interface
+ .DIG_IN(DIG_IN),
+ .DIG_OUT(DIG_OUT),
+ .DIG_RCLK(DIG_RCLK),
+ .DIG_SAMPLE(DIG_SAMPLE),
+ .DIG_SCLK(DIG_SCLK),
+ .DIG_SRLOAD(DIG_SRLOAD)
+ );
+
///////////////////////////////////////////
/////// DDR2 core 128 MB x 16 of memory, 312 MHz (624 Mt/s = 1.2 GiB/s)
@@ -535,14 +616,18 @@ module kovan (
//////////////////////////////////////
reg [22:0] counter;
+ reg clk3p2M_unbuf;
always @(posedge clk26buf) begin
counter <= counter + 1;
+ clk3p2M_unbuf <= counter[2];
`ifdef HDMI
HDCP_AKSV <= Aksv14_write; // retime it into this domain to not screw up timing closure
`endif
end
+ BUFG clk3p2M_buf(.I(clk3p2M_unbuf), .O(clk3p2M));
+
////////////////////////////////
// serial number
@@ -801,15 +886,95 @@ module kovan (
//
// register 0x38-0x3e: device ID (7 bytes)
//
- // register 0x3f: version number
+ // register 0x3f: version number. Note that value 0xFF means to refer to extended version number
///////////////// EXTENDED REGISTER SET
- // register 0x50-53 is write-only: 32-bit test data to write to DDR2 (little endian)
- // register 0x54-57 is write-only: 32-bit test address for all DDR2 operations (read and write)
+ // registers with addresses 0x40-0x7F are write-only
+ // registers with addresses 0x80-0xFF are read-only
+ /////////////////
+ //
+ // register 0x40: digital output values for digital bits 7:0
+ // register 0x41: output enable for digital bits 7:0
+ // register 0x42: pullup enables for digital bits 7:0
+ // register 0x43: analog pullup enables for analog bits 7:0
+ // register 0x44: digital input values for digital bits 7:0
+ //
+ /////////////////
+ // register 0x45: digital shift chain control
+ // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
+ // | | | | | | SAMPLE | UPDTE
+ // bit 0: on rising edge, update the digital pins with the loaded register values
+ // bit 1: on rising edge, sample all the digital inputs simultaneously
+ //
+ /////////////////
+ // register 0x46: ADC control
+ // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
+ // | | | GO | CHAN3 | CHAN2 | CHAN1 | CHAN0
+ // bits 2-0: channel of ADC converter to convert
+ // bit 3: 1 selects user ADC, 0 selects motor current fbk ADC
+ //
+ /////////////////
+ // register 0x47: motor control
+ // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
+ // | | | | | | | ALLSTP
+ // bit 0: when set, all motors are immediately stopped
+ //
+ /////////////////
+ // register 0x48: motor direction
+ // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
+ // M3D1 | M3D0 | M2D1 | M2D0 | M1D1 | M1D0 | M0D1 | M0D0
+ // for all direction 2-bit codes, 10 = forward, 01 = reverse, 11 = short brake, 00 = stop
+ // note: short brake shorts both motor terminals to ground
+ // stop just shorts one terminal to ground
+ // bits 1-0: motor 0 direction
+ // bits 3-2: motor 1 direction
+ // bits 5-4: motor 2 direction
+ // bits 7-6: motor 3 direction
+ //
+ /////////////////
+ // register 49-4a: motor PWM duty cycle (MDUTY)
+ // 12-bit value specifying the duty cycle of the motor PWM
+ // A 12-bit value allows a minimu duty cycle resolution of 1/4095 = 0.02%
+ //
+ /////////////////
+ // register 4c-4b: motor PWM divider (MDIV)
+ // 16-bit vaule specifying the divider for the motor PWM
+ // The base clock for the motor PWM is 26 MHz / 4096. The final period for the PWM is thus:
+ // 6.347kHz / ( MDIV + 1 )
+ //
+ /////////////////
+ // register 4d-4f: servo PWM period (SPERIOD)
+ // 24-bit value which specifies the length of the PWM period for the servo
+ // Servo PWM is custom-built for specifying sparse, high-resolution narrow pulse widths.
+ // The period for the servo is defined to be:
+ // 26 MHz / (SPERIOD + 1)
+ // As SPERIOD is a 24-bit number, the longest period is thus 1.5 Hz.
+ //
+ /////////////////
+ // register 50-52: servo 0 pulse width (S0PULSE)
+ // 24-bit value which specifies the width of the servo pulse. The quanta for the value is
+ // 1/26 MHz = 38.4ns
+ // Please note that the pulse width is not a percentage duty cycle, but an absolute time specifier
+ //
+ /////////////////
+ // register 53-55: servo 1 pulse width (S1PULSE)
+ // see servo 0 description
//
/////////////////
- // register 0x58 is write-only:
+ // register 58-56: servo 2 pulse width (S2PULSE)
+ // see servo 0 description
+ //
+ /////////////////
+ // register 5b-59: servo 3 pulse width (S3PULSE)
+ // see servo 0 description
+ //
+ /////////////////
+ // register 0x60-63: 32-bit test data to write to DDR2 (little endian)
+ // register 0x64-67: 32-bit test address for all DDR2 operations (read and write)
+ //
+ /////////////////
+ // register 0x68 is write-only:
// control commands for DDR2 interfaces
// bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
// | | | | | | DO_CMD | RD_WR
@@ -817,22 +982,50 @@ module kovan (
// bit 1: start the requested command now
//
/////////////////
+ //
+ // begin read-only setion
+ //
+ /////////////////
+ // register 0x80 is the digital I/O status register
+ // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
+ // | | | | | | DGOOD | DBUSY
+ // bit 0: 1 indicates that the digital shift chain is busy
+ // bit 1: 1 indicates that the digital input value is good
+ // (i.e., updated from most recent sample request)
+ //
+ /////////////////
+ // register 0x81-82 is the ADC value register (lower 10 bits only)
+ //
+ /////////////////
+ // register 0x83 is the ADC status register
+ // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
+ // | | | | | | | AVALID
+ // bit 0: 1 means that the ADC in value is valid. Cleared when ADC "GO" is triggered.
+ // insensitive to changes on channel specifier.
+ //
+ /////////////////
// register 0x90-93 is read-only: 32-bit read data from DDR2
//
/////////////////
- // register 0x94 is read-only:
- // status for DDR2 control interface
+ //
+ // register 0x94 is the status for DDR2 control interface
// bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0
// | | | | | | WREMPTY | RDAVAIL
// bit 0: indicates that read data is available
// bit 1: indicates that the write FIFO is empty
//
/////////////////
- // register 0x95 is read-only:
- // DDR2 state machine debug
+ // register 0x95 is for DDR2 state machine debug
// bits 7-4: wr machine
// bits 3-0: rd machine
//
+ /////////////////
+ // register 0xfc-0xff are the extended version code
+ // 0xfc-0xfd is the implementation revision (16 bits)
+ // 0xfe-0xff is the machine code (16 bits):
+ // 0x0000: reserved
+ // 0x0001: Kovan
+ //
wire SDA_pd;
wire [7:0] reg_addr;
@@ -960,19 +1153,55 @@ module kovan (
// reg 40 - reg 60 are write registers (32 locations, growable to 64)
// reg 80 - reg C0 are read-only registers (64 locations)
// write-only interfaces
- .reg_40( ),
- .reg_50(ddr2_write_data[7:0]),
- .reg_51(ddr2_write_data[15:8]),
- .reg_52(ddr2_write_data[23:16]),
- .reg_53(ddr2_write_data[31:24]),
- .reg_54(ddr2_test_addr[7:0]),
- .reg_55(ddr2_test_addr[15:8]),
- .reg_56(ddr2_test_addr[23:16]),
- .reg_57(ddr2_test_addr[31:24]),
- .reg_58(ddr2_regcmd[7:0]),
+ .reg_40(dig_out_val),
+ .reg_41(dig_oe),
+ .reg_42(dig_pu),
+ .reg_43(ana_pu),
+ .reg_44(dig_in_val),
+ .reg_45({dig_sample,dig_update}),
+ .reg_46({adc_go,adc_chan[3:0]}),
+ .reg_47({mot_allstop}),
+ .reg_48(mot_drive_code),
+ .reg_49(mot_pwm_duty[7:0]),
+ .reg_4a(mot_pwm_duty[15:8]),
+ .reg_4b(mot_pwm_div[7:0]),
+ .reg_4c(mot_pwm_div[15:8]),
+
+ .reg_4d(servo_pwm_period[7:0]),
+ .reg_4e(servo_pwm_period[15:8]),
+ .reg_4f(servo_pwm_period[23:16]),
+
+ .reg_50(servo0_pwm_pulse[7:0]),
+ .reg_51(servo0_pwm_pulse[15:8]),
+ .reg_52(servo0_pwm_pulse[23:16]),
+
+ .reg_53(servo1_pwm_pulse[7:0]),
+ .reg_54(servo1_pwm_pulse[15:8]),
+ .reg_55(servo1_pwm_pulse[23:16]),
+
+ .reg_56(servo2_pwm_pulse[7:0]),
+ .reg_57(servo2_pwm_pulse[15:8]),
+ .reg_58(servo2_pwm_pulse[23:16]),
+
+ .reg_59(servo3_pwm_pulse[7:0]),
+ .reg_5a(servo3_pwm_pulse[15:8]),
+ .reg_5b(servo3_pwm_pulse[23:16]),
+
+ .reg_60(ddr2_write_data[7:0]),
+ .reg_61(ddr2_write_data[15:8]),
+ .reg_62(ddr2_write_data[23:16]),
+ .reg_63(ddr2_write_data[31:24]),
+ .reg_64(ddr2_test_addr[7:0]),
+ .reg_65(ddr2_test_addr[15:8]),
+ .reg_66(ddr2_test_addr[23:16]),
+ .reg_67(ddr2_test_addr[31:24]),
+ .reg_68(ddr2_regcmd[7:0]),
// read-only interfaces
- .reg_80( ),
+ .reg_80({dig_val_good, dig_busy}),
+ .reg_81(adc_in[7:0]),
+ .reg_82(6'b000000,adc_in[9:8]),
+ .reg_83({adc_valid}),
.reg_90(ddr2_read_data[7:0]),
.reg_91(ddr2_read_data[15:8]),
@@ -982,11 +1211,11 @@ module kovan (
.reg_95(ddr2_sm_dbg[7:0]),
/// extened version -- 32 bits to report versions
- /// kovan starts at FF.01.01.01.01
+ /// kovan starts at FF.00.01.00.01
.reg_fc(8'h1), // this is the LSB of the extended version field
- .reg_fd(8'h1),
+ .reg_fd(8'h0),
.reg_fe(8'h1),
- .reg_ff(8'h1) // this is the MSB of the extended version field
+ .reg_ff(8'h0) // this is the MSB of the extended version field
);
/////// version 4 changes
View
452 kovan1.srcs/sources_1/imports/kovan1/robot_iface.v
@@ -0,0 +1,452 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011, Andrew "bunnie" Huang
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation and/or
+// other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+`timescale 1 ns / 1 ps
+
+module robot_iface(
+ input wire clk,
+ input wire clk_3p2MHz,
+ input wire glbl_reset,
+
+ // digital i/o block
+ input wire [7:0] dig_out_val,
+ input wire [7:0] dig_oe,
+ input wire [7:0] dig_pu,
+ input wire [7:0] ana_pu,
+ output reg [7:0] dig_in_val,
+ output wire dig_val_good, // output value is valid when high
+ output wire dig_busy, // chain is busy when high
+ input wire dig_sample, // samples input on rising edge
+ input wire dig_update, // updates chain on rising edge
+
+ // ADC interface
+ output reg [9:0] adc_in,
+ input wire [3:0] adc_chan, // channels 0-7 are for user, 8-15 are for motor current fbk
+ output wire adc_valid,
+ input wire adc_go,
+
+ // motor driver interface
+ input wire [15:0] mot_pwm_div,
+ input wire [15:0] mot_pwm_duty,
+ input wire [7:0] mot_drive_code, // 2 bits/chan, 00 = stop, 01 = forward, 10 = rev, 11 = stop
+ input wire mot_allstop,
+
+ // servo interface
+ input wire [23:0] servo_pwm_period, // total period for the servo update
+ input wire [23:0] servo0_pwm_pulse, // pulse width in absolute time
+ input wire [23:0] servo1_pwm_pulse,
+ input wire [23:0] servo2_pwm_pulse,
+ input wire [23:0] servo3_pwm_pulse,
+
+ /////// physical interfaces to outside the chip
+ // motors
+ output reg [3:0] MBOT,
+ output reg [3:0] MTOP,
+ output wire MOT_EN,
+ output wire [3:0] M_SERVO,
+
+ // analog interface
+ output wire [1:0] DIG_ADC_CS,
+ output wire DIG_ADC_IN,
+ input wire DIG_ADC_OUT,
+ output wire DIG_ADC_SCLK,
+
+ // digital interface
+ output wire DIG_IN,
+ input wire DIG_OUT,
+ output wire DIG_RCLK,
+ output wire DIG_SAMPLE,
+ output wire DIG_SCLK,
+ output wire DIG_SRLOAD
+ );
+
+ ///////////////// digital i/o interface and pull-up control
+ reg go;
+ reg go_d;
+ reg go_edge;
+
+ reg dinsamp;
+
+ wire motor_reset;
+ sync_reset motor_reset_sync(
+ .clk(clk),
+ .glbl_reset(glbl_reset),
+ .reset(motor_reset) );
+
+ assign DIG_SAMPLE = dinsamp;
+
+ always @(posedge clk) begin
+ go <= dig_update;
+ go_d <= go;
+ go_edge <= go & !go_d;
+
+ dinsamp <= dig_sample;
+ end // always @ (posedge clk)
+
+ parameter SR_INIT = 5'b1 << 0;
+ parameter SR_START_DIG = 5'b1 << 1;
+ parameter SR_SHIFT_DIG = 5'b1 << 2;
+ parameter SR_SHIFT_DIG_TERM = 5'b1 << 3;
+ parameter SR_DIG_DONE = 5'b1 << 4;
+
+ parameter SR_nSTATES = 5;
+
+ reg [(SR_nSTATES-1):0] SR_cstate = {{(SR_nSTATES-1){1'b0}}, 1'b1};
+ reg [(SR_nSTATES-1):0] SR_nstate;
+
+ reg [5:0] shift_count;
+ reg dig_srload;
+ reg [39:0] shift_in;
+ reg [31:0] shift_out;
+ reg update_dig;
+ reg busy;
+ reg dgood;
+
+
+ always @ (negedge clk) begin
+ if (motor_reset)
+ SR_cstate <= SR_INIT;
+ else
+ SR_cstate <= SR_nstate;
+ end
+
+ always @ (*) begin
+ case (SR_cstate) //synthesis parallel_case full_case
+ SR_INIT: begin
+ if( go_edge ) begin
+ SR_nstate = SR_START_DIG;
+ end else begin
+ SR_nstate = SR_INIT;
+ end
+ end // case: SR_INIT
+
+ SR_START_DIG: begin
+ SR_nstate = SR_SHIFT_DIG;
+ end
+
+ SR_SHIFT_DIG: begin
+ SR_nstate = (shift_count[5:0] == 6'h1e) ? SR_SHIFT_DIG_TERM : SR_SHIFT_DIG;
+ end
+
+ SR_SHIFT_DIG_TERM: begin
+ SR_nstate = SR_DIG_DONE;
+ end
+
+ SR_DIG_DONE: begin
+ SR_nstate = SR_INIT;
+ end
+
+ endcase // case (SR_cstate)
+ end
+
+ assign dig_busy = busy;
+ assign dig_val_good = dgood;
+
+ //// note, we assume that DIG_SAMPLE is driven by user before this chain is triggered
+ //// the split enables very precise user control over when sampling happens, versus readout
+ wire [7:0] dig_oe_n;
+ assign dig_oe_n[7:0] = ~dig_oe[7:0];
+
+ always @ (posedge clk) begin
+ case (SR_cstate) //synthesis parallel_case full_case
+ SR_INIT: begin
+ shift_count <= 6'b0;
+ shift_in <= 40'b0;
+ shift_out <= 32'b1111_1111_1111_1111_1111_1111_1111_1111;
+
+ dig_srload <= 1'b1;
+ update_dig <= 1'b1;
+ dig_in_val <= dig_in_val;
+
+ if( dinsamp ) begin
+ dgood <= 1'b0;
+ end else begin
+ dgood <= dgood;
+ end
+
+ busy <= busy;
+ end
+ SR_START_DIG: begin
+ shift_count <= 6'b0;
+ shift_out <= {ana_pu[7:0],dig_pu[7:0],dig_oe_n[7:0],dig_out_val[7:0]};
+ shift_in <= shift_in;
+
+ dig_srload <= chain_detect; // if chain detect is high, we never update the digital SRs
+ update_dig <= 1'b1;
+ dig_in_val <= dig_in_val;
+
+ dgood <= 1'b0;
+ busy <= 1'b1;
+ end
+ SR_SHIFT_DIG: begin
+ shift_count <= shift_count + 6'b1;
+ shift_in <= {shift_in[38:0],DIG_OUT};
+ shift_out <= {shift_out[30:0],1'b1};
+
+ dig_srload <= 1'b1;
+ update_dig <= 1'b1;
+ dig_in_val <= dig_in_val;
+
+ dgood <= dgood;
+ busy <= 1'b1;
+ end
+ SR_SHIFT_DIG_TERM: begin
+ shift_count <= shift_count + 6'b1;
+ shift_in <= {shift_in[38:0],DIG_OUT};
+ shift_out <= {shift_out[30:0],1'b1};
+
+ dig_srload <= 1'b1;
+ update_dig <= 1'b0;
+ dig_in_val <= dig_in_val;
+
+ dgood <= dgood;
+ busy <= 1'b1;
+ end
+ SR_DIG_DONE: begin
+ shift_count <= shift_count;
+ shift_in <= shift_in;
+ shift_out <= shift_out;
+
+ dig_srload <= 1'b1;
+ update_dig <= 1'b1;
+ dig_in_val <= shift_in[14:7];
+
+ dgood <= 1'b1;
+ busy <= 1'b0;
+ end
+ endcase // case (SR_cstate)
+ end // always @ (posedge clk)
+
+ assign DIG_SCLK = !clk;
+ assign DIG_IN = shift_out[31];
+ assign DIG_RCLK = update_dig;
+ // OR srload with ! clock, so as to avoid a race condition with rising edge flops,
+ // i.e. you don't want to be loading the flop any where near the shifting of the flop
+ assign DIG_SRLOAD = dig_srload | clk;
+
+
+ /////////////////////////////////
+ //////////////////////// PWM block
+ /////////////////////////////////
+
+ ///////// motor PWM prescaler
+ reg pwm_clk;
+ reg pwm_clk_a;
+ reg [15:0] pwm_scaler;
+ wire pwmclk;
+
+ always @(posedge clk) begin
+ if( (pwm_scaler > mot_pwm_div) || (&pwm_scaler) ) begin
+ pwm_scaler <= 0;
+ pwm_clk_a <= 1;
+ end else begin
+ pwm_clk_a <= 0;
+ pwm_scaler <= pwm_scaler + 1;
+ end
+ end // always @ (posedge clk or posedge reset)
+
+ always @(posedge clk) begin
+ pwm_clk <= pwm_clk_a; // just to clean everything up, no glitches on clock
+ end
+
+ BUFG pwmclkbuf(.I(pwm_clk), .O(pwmclk));
+
+ ///////// PWM for motors -- one PWM for all four channels
+ mot_pwm mot1_chan (.clk(pwmclk),
+ .duty_cycle(mot_pwm_duty[11:0]), // note only 12 bits used, set in lower module
+ .pwm_output(MOT_EN));
+
+ ///////// motor direction controls
+ always @(posedge clk) begin
+ MBOT[0] <= ((mot_drive_code[1:0] == 2'b10) ? 1'b1 : 1'b0) & !mot_allstop;
+ MTOP[0] <= ((mot_drive_code[1:0] == 2'b01) ? 1'b1 : 1'b0) & !mot_allstop;
+
+ MBOT[1] <= ((mot_drive_code[3:2] == 2'b10) ? 1'b1 : 1'b0) & !mot_allstop;
+ MTOP[1] <= ((mot_drive_code[3:2] == 2'b01) ? 1'b1 : 1'b0) & !mot_allstop;
+
+ MBOT[2] <= ((mot_drive_code[5:4] == 2'b10) ? 1'b1 : 1'b0) & !mot_allstop;
+ MTOP[2] <= ((mot_drive_code[5:4] == 2'b01) ? 1'b1 : 1'b0) & !mot_allstop;
+
+ MBOT[3] <= ((mot_drive_code[7:6] == 2'b10) ? 1'b1 : 1'b0) & !mot_allstop;
+ MTOP[3] <= ((mot_drive_code[7:6] == 2'b01) ? 1'b1 : 1'b0) & !mot_allstop;
+ end
+
+ // servo channels
+ wire [3:0] m_servo_out;
+ servo_pwm servo0_chan (.clk(clk),
+ .period(servo_pwm_period),
+ .pulse(servo0_pwm_pulse),
+ .pwm_output(m_servo_out[0]));
+
+ servo_pwm servo1_chan (.clk(clk),
+ .period(servo_pwm_period),
+ .pulse(servo1_pwm_pulse),
+ .pwm_output(m_servo_out[1]));
+
+ servo_pwm servo2_chan (.clk(clk),
+ .period(servo_pwm_period),
+ .pulse(servo2_pwm_pulse),
+ .pwm_output(m_servo_out[2]));
+
+ servo_pwm servo3_chan (.clk(clk),
+ .period(servo_pwm_period),
+ .pulse(servo3_pwm_pulse),
+ .pwm_output(m_servo_out[3]));
+
+ assign M_SERVO[0] = !m_servo_out[0]; // invert to compensate inverting level converters
+ assign M_SERVO[1] = !m_servo_out[1];
+ assign M_SERVO[2] = !m_servo_out[2];
+ assign M_SERVO[3] = !m_servo_out[3];
+
+
+ /////////////////////////////////
+ //////////////////////// ADC block
+ /////////////////////////////////
+ parameter ADC_INIT = 5'b1 << 0;
+ parameter ADC_START = 5'b1 << 1;
+ parameter ADC_SHIFT = 5'b1 << 2;
+ parameter ADC_SHIFT_TERM = 5'b1 << 3;
+ parameter ADC_DONE = 5'b1 << 4;
+
+ parameter ADC_nSTATES = 5;
+
+ reg [(ADC_nSTATES-1):0] ADC_cstate = {{(ADC_nSTATES-1){1'b0}}, 1'b1};
+ reg [(ADC_nSTATES-1):0] ADC_nstate;
+
+ wire motor_reset_3p2;
+ sync_reset motor_reset_sync_3p2(
+ .clk(clk_3p2MHz),
+ .glbl_reset(glbl_reset),
+ .reset(motor_reset_3p2) );
+
+
+ reg adc_go_d;
+ reg adc_go_edge;
+ reg [4:0] adc_shift_count;
+ reg adc_valid;
+ reg [15:0] adc_shift_out;
+ reg [15:0] adc_shift_in;
+ reg [1:0] adc_cs;
+
+ always @(posedge clk_3p2MHz) begin
+ adc_go_d <= adc_go;
+ adc_go_edge <= adc_go & !adc_go_d;
+ end // always @ (posedge clk)
+
+ always @ (posedge clk_3p2MHz) begin
+ if (motor_reset_3p2)
+ ADC_cstate <= ADC_INIT;
+ else
+ ADC_cstate <= ADC_nstate;
+ end
+
+ always @ (*) begin
+ case (ADC_cstate) //synthesis parallel_case full_case
+ ADC_INIT: begin
+ if( adc_go_edge ) begin
+ ADC_nstate = ADC_START;
+ end else begin
+ ADC_nstate = ADC_INIT;
+ end
+ end // case: ADC_INIT
+
+ ADC_START: begin
+ ADC_nstate = ADC_SHIFT;
+ end
+
+ ADC_SHIFT: begin
+ ADC_nstate = (adc_shift_count[4:0] == 5'he) ? ADC_SHIFT_TERM : ADC_SHIFT;
+ end
+
+ ADC_SHIFT_TERM: begin
+ ADC_nstate = ADC_DONE;
+ end
+
+ ADC_DONE: begin
+ ADC_nstate = ADC_INIT;
+ end
+
+ endcase // case (ADC_cstate)
+ end
+
+
+ always @ (posedge clk_3p2MHz) begin
+ case (ADC_cstate) //synthesis parallel_case full_case
+ ADC_INIT: begin
+ adc_shift_count <= 5'b0;
+ adc_shift_out <= 32'b1111_1111_1111_1111;
+ adc_shift_in <= 16'b0;
+ adc_cs <= 2'b11;
+
+ adc_valid <= adc_valid;
+ adc_in <= adc_in;
+ end
+ ADC_START: begin
+ adc_shift_count <= 5'b0;
+ adc_shift_out <= {11'b0,adc_chan[0:2],2'b0};
+ adc_shift_in <= adc_shift_in;
+ adc_cs <= adc_chan[3] ? 2'b01 : 2'b10;
+
+ adc_valid <= 0;
+ adc_in <= adc_in;
+ end
+ ADC_SHIFT: begin
+ adc_shift_count <= adc_shift_count + 6'b1;
+ adc_shift_out <= {adc_shift_out[14:0],1'b1};
+ adc_shift_in <= {adc_shift_in[14:0], DIG_ADC_OUT};
+ adc_cs <= adc_cs;
+
+ adc_valid <= 0;
+ adc_in <= adc_in;
+ end
+ ADC_SHIFT_TERM: begin
+ adc_shift_count <= adc_shift_count + 6'b1;
+ adc_shift_out <= {adc_shift_out[14:0],1'b1};
+ adc_shift_in <= {adc_shift_in[14:0], DIG_ADC_OUT};
+ adc_cs <= adc_cs;
+
+ adc_valid <= 0;
+ adc_in <= adc_in;
+ end
+ ADC_DONE: begin
+ adc_shift_count <= adc_shift_count;
+ adc_shift_out <= adc_shift_out;
+ adc_shift_in <= adc_shift_in;
+ adc_cs <= 2'b11;
+
+ adc_valid <= 1;
+ adc_in <= adc_shift_in[13:4];
+ end
+ endcase // case (ADC_cstate)
+ end // always @ (posedge clk)
+
+ assign DIG_ADC_SCLK = !clk_3p2MHz;
+ assign DIG_ADC_IN = adc_shift_out[15];
+ assign DIG_ADC_CS[1:0] = adc_cs[1:0];
+
+endmodule // robot_iface
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.