blob: d9a43c9e1f852e924c0c7446047f699f5c9a5fe0 [file] [log] [blame]
module PWMDevice #(
parameter ID = 4'h0,
parameter OUTPUTS = 4,
parameter WIDTH = 16,
parameter CLOCK_WIDTH = 32
)(
input wire clk,
input wire rst,
// Peripheral bus
input wire peripheralEnable,
input wire peripheralBus_we,
input wire peripheralBus_oe,
output wire peripheralBus_busy,
input wire[15:0] peripheralBus_address,
input wire[3:0] peripheralBus_byteSelect,
output reg[31:0] peripheralBus_dataRead,
input wire[31:0] peripheralBus_dataWrite,
output wire requestOutput,
// PWM output
output wire[OUTPUTS-1:0] pwm_en,
output wire[OUTPUTS-1:0] pwm_out,
output wire pwm_irq
);
localparam CLOCK_BITS = $clog2(CLOCK_WIDTH);
wire counterEnable;
wire[CLOCK_BITS-1:0] clockScale;
wire[OUTPUTS-1:0] compareEnable;
wire[OUTPUTS-1:0] outputEnable;
wire[OUTPUTS-1:0] riseInterruptEnable;
wire[OUTPUTS-1:0] fallInterruptEnable;
// Counter control
reg[CLOCK_WIDTH + WIDTH - 1:0] baseCounter = 'b0;
wire[CLOCK_WIDTH + WIDTH - 1:0] nextCounter = baseCounter + 1;
wire[WIDTH-1:0] counterValue = baseCounter >> clockScale;
// Device select
wire[11:0] localAddress;
wire deviceEnable;
DeviceSelect #(.ID(ID)) select(
.peripheralEnable(peripheralEnable),
.peripheralBus_address(peripheralBus_address),
.localAddress(localAddress),
.deviceEnable(deviceEnable));
// Register
// Configuration register Default 0x000003 (for .CLOCK_WIDTH(32))
// b00-b04: clockScale Default 0x03
// b05: counterEnable Default 0x0
// b06: compareEnable0 Default 0x0
// b07: compareEnable1 Default 0x0
// b08: compareEnable2 Default 0x0
// b09: compareEnable3 Default 0x0
// b10: outputEnable0 Default 0x0
// b11: outputEnable1 Default 0x0
// b12: outputEnable2 Default 0x0
// b13: outputEnable3 Default 0x0
// b14: riseInterruptEnable0 Default 0x0
// b15: riseInterruptEnable1 Default 0x0
// b16: riseInterruptEnable2 Default 0x0
// b17: riseInterruptEnable3 Default 0x0
// b18: fallInterruptEnable0 Default 0x0
// b19: fallInterruptEnable1 Default 0x0
// b20: fallInterruptEnable2 Default 0x0
// b21: fallInterruptEnable3 Default 0x0
localparam CONFIG_WIDTH = 1 + CLOCK_BITS + (OUTPUTS * 4);
wire[CONFIG_WIDTH-1:0] configuration;
wire[31:0] configurationRegisterOutputData;
wire configurationRegisterOutputRequest;
ConfigurationRegister #(.WIDTH(CONFIG_WIDTH), .ADDRESS(12'h000), .DEFAULT({ {(CONFIG_WIDTH-5){1'b0}}, 5'h0E })) configurationRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(configurationRegisterOutputData),
.requestOutput(configurationRegisterOutputRequest),
.currentValue(configuration));
assign clockScale = configuration[CLOCK_BITS-1:0];
assign counterEnable = configuration[CLOCK_BITS];
assign compareEnable = configuration[(1 + CLOCK_BITS + OUTPUTS)-1:1 + CLOCK_BITS];
assign outputEnable = configuration[(1 + CLOCK_BITS + (OUTPUTS * 2))-1:1 + CLOCK_BITS + OUTPUTS];
assign riseInterruptEnable = configuration[(1 + CLOCK_BITS + (OUTPUTS * 3))-1:1 + CLOCK_BITS + (OUTPUTS * 2)];
assign fallInterruptEnable = configuration[(1 + CLOCK_BITS + (OUTPUTS * 4))-1:1 + CLOCK_BITS + (OUTPUTS * 3)];
// Counter top compare Default 0x1388 (for .WIDTH(16))
// With clockScale=0x03, this gives a 1kHz signal with a 200ns resolution
localparam DEFAULT_TOP_COMPARE_VALUE = 'h1388;
wire[31:0] topCompareRegisterOutputData;
wire topCompareRegisterOutputRequest;
wire topCompareRegisterBusBusy_nc;
wire[WIDTH-1:0] topCompareRegisterWriteData;
wire topCompareRegisterWriteDataEnable;
wire topCompareRegisterReadDataEnable_nc;
reg[WIDTH-1:0] topCompare;
DataRegister #(.WIDTH(WIDTH), .ADDRESS(12'h004)) topCompareRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_busy(topCompareRegisterBusBusy_nc),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(topCompareRegisterOutputData),
.requestOutput(topCompareRegisterOutputRequest),
.writeData(topCompareRegisterWriteData),
.writeData_en(topCompareRegisterWriteDataEnable),
.writeData_busy(1'b0),
.readData(topCompare),
.readData_en(topCompareRegisterReadDataEnable_nc),
.readData_busy(1'b0));
// Current data register (for .WIDTH(16), .OUTPUTS(4))
// b00-b15: counterValue
// b16-b19: output
wire[OUTPUTS-1:0] outputs;
wire[31:0] dataRegisterOutputData;
wire dataRegisterOutputRequest;
wire dataRegisterBusBusy_nc;
wire[WIDTH+OUTPUTS-1:0] dataRegisterWriteData_nc;
wire dataRegisterWriteDataEnable_nc;
wire dataRegisterReadDataEnable_nc;
DataRegister #(.WIDTH(WIDTH + OUTPUTS), .ADDRESS(12'h008)) dataRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_busy(dataRegisterBusBusy_nc),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(dataRegisterOutputData),
.requestOutput(dataRegisterOutputRequest),
.writeData(dataRegisterWriteData_nc),
.writeData_en(dataRegisterWriteDataEnable_nc),
.writeData_busy(1'b0),
.readData({ outputs, counterValue }),
.readData_en(dataRegisterReadDataEnable_nc),
.readData_busy(1'b0));
always @(posedge clk) begin
if (rst) begin
baseCounter <= 'b0;
topCompare <= DEFAULT_TOP_COMPARE_VALUE;
end else begin
if (topCompareRegisterWriteDataEnable) begin
baseCounter <= 'b0;
topCompare <= topCompareRegisterWriteData;
end else begin
if (counterEnable) begin
if (counterValue == topCompare) baseCounter <= 'b0;
else baseCounter <= nextCounter;
end else begin
baseCounter <= 'b0;
end
end
end
end
wire[OUTPUTS-1:0] compareRegisterOutputRequest;
wire[(32 * OUTPUTS) - 1:0] compareRegisterOutputData;
wire[31:0] compareValuesOutputData;
wire compareValuesOutputRequest;
Mux #(.WIDTH(32), .INPUTS(OUTPUTS), .DEFAULT(~32'b0)) mux(
.select(compareRegisterOutputRequest),
.in(compareRegisterOutputData),
.out(compareValuesOutputData),
.outputEnable(compareValuesOutputRequest));
assign requestOutput = configurationRegisterOutputRequest || topCompareRegisterOutputRequest || dataRegisterOutputRequest || compareValuesOutputRequest;
always @(*) begin
case (1'b1)
configurationRegisterOutputRequest: peripheralBus_dataRead <= configurationRegisterOutputData;
topCompareRegisterOutputRequest: peripheralBus_dataRead <= topCompareRegisterOutputData;
dataRegisterOutputRequest: peripheralBus_dataRead <= dataRegisterOutputData;
compareValuesOutputRequest: peripheralBus_dataRead <= compareValuesOutputData;
default: peripheralBus_dataRead <= 32'b0;
endcase
end
wire[OUTPUTS-1:0] compareRise;
wire[OUTPUTS-1:0] compareFall;
// Outputs
genvar i;
generate
for (i = 0; i < OUTPUTS; i = i + 1) begin
// Compare value register
wire[WIDTH-1:0] compareValue;
ConfigurationRegister #(.WIDTH(WIDTH), .ADDRESS(12'h010 + (i * 12'h004)), .DEFAULT('b0)) compareRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(compareRegisterOutputData[(i * 32) + 31:i * 32]),
.requestOutput(compareRegisterOutputRequest[i]),
.currentValue(compareValue));
PWMOutput #(.WIDTH(WIDTH)) outputPort(
.clk(clk),
.rst(rst),
.compareValue(compareValue),
.enable(compareEnable[i]),
.counterValue(counterValue),
.pwm_out(outputs[i]),
.compareRise(compareRise[i]),
.compareFall(compareFall[i]));
end
endgenerate
assign peripheralBus_busy = 1'b0;
assign pwm_en = compareEnable & outputEnable;
assign pwm_out = outputs;
wire[OUTPUTS-1:0] comparatorIRQ = (riseInterruptEnable & compareRise) || (fallInterruptEnable & compareFall);
assign pwm_irq = |comparatorIRQ;
endmodule