OpenCores
URL https://opencores.org/ocsvn/psg16/psg16/trunk

Subversion Repositories psg16

[/] [psg16/] [trunk/] [rtl/] [verilog/] [AC97Controller.v] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 robfinch
`timescale 1ns / 1ps
2
//=============================================================================
3
// (C) 2012 Robert Finch
4
//      All rights reserved.
5
//
6
// AC97.v
7
//      AC97 controller interface to LM4550
8
//
9
// This source file is free software: you can redistribute it and/or modify 
10
// it under the terms of the GNU Lesser General Public License as published 
11
// by the Free Software Foundation, either version 3 of the License, or     
12
// (at your option) any later version.                                      
13
//                                                                          
14
// This source file is distributed in the hope that it will be useful,      
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of           
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            
17
// GNU General Public License for more details.                             
18
//                                                                          
19
// You should have received a copy of the GNU General Public License        
20
// along with this program.  If not, see <http://www.gnu.org/licenses/>.    
21
//
22
//=============================================================================
23
 
24
//
25
// This core uses a shadow register set that would be mapped into the I/O space
26
// of the host controller to hold the contents of the LM4550 registers. The core
27
// tracks updates to the LM4550 registers and writes the updates out as the
28
// AC97 frame becomes available.
29
//
30
module AC97(rst_i, clk_i, cyc_i, stb_i, ack_o, we_i, adr_i, dat_i, dat_o,
31
        PSGout,
32
        BIT_CLK, SYNC, SDATA_IN, SDATA_OUT, RESET
33
);
34
input rst_i;
35
input clk_i;
36
input cyc_i;
37
input stb_i;
38
output ack_o;
39
input we_i;
40
input [63:0] adr_i;
41
input [15:0] dat_i;
42
output [15:0] dat_o;
43
reg [15:0] dat_o;
44
 
45
input [17:0] PSGout;
46
 
47
input BIT_CLK;
48
output SYNC;
49
input SDATA_IN;
50
output SDATA_OUT;
51
output RESET;
52
 
53
wire cs = cyc_i && stb_i && (adr_i[63:8]==56'hFFFF_FFFF_FFDC_10);
54
reg ack1;
55
always @(posedge clk_i)
56
        ack1 <= cs & !ack1;
57
assign ack_o = cs ? (we_i ? 1'b1 : ack1) : 1'b0;
58
 
59
reg codecReady;
60
reg [15:0] slot0;
61
reg [19:0] slot1;
62
reg [19:0] slot2;
63
reg [19:0] slot3;
64
reg [19:0] slot4;
65
reg [19:0] slot5;
66
reg [19:0] slot6;
67
reg [19:0] slot7;
68
reg [19:0] slot8;
69
reg [19:0] slot9;
70
reg [19:0] slot10;
71
reg [19:0] slot11;
72
reg [19:0] slot12;
73
reg [4:0] rgno;
74
 
75
wire [15:0] slot0i;
76
wire [19:0] slot1i;
77
wire [19:0] slot2i;
78
reg [15:0] reg26;
79
 
80
// There's really only 27 registers in the LM4550. A couple of address maps are
81
// used to compress the register addresses so that a smaller shadow RAM can be
82
// used.
83
function [6:0] fnRealToLM4550RegMap;
84
input [4:0] realreg;
85
begin
86
case(realreg)
87
5'd0:   fnRealToLM4550RegMap = 7'h00;
88
5'd1:   fnRealToLM4550RegMap = 7'h02;
89
5'd2:   fnRealToLM4550RegMap = 7'h04;
90
5'd3:   fnRealToLM4550RegMap = 7'h06;
91
5'd4:   fnRealToLM4550RegMap = 7'h0A;
92
5'd5:   fnRealToLM4550RegMap = 7'h0C;
93
5'd6:   fnRealToLM4550RegMap = 7'h0E;
94
5'd7:   fnRealToLM4550RegMap = 7'h10;
95
5'd8:   fnRealToLM4550RegMap = 7'h12;
96
5'd9:   fnRealToLM4550RegMap = 7'h14;
97
5'd10:  fnRealToLM4550RegMap = 7'h16;
98
5'd11:  fnRealToLM4550RegMap = 7'h18;
99
5'd12:  fnRealToLM4550RegMap = 7'h1A;
100
5'd13:  fnRealToLM4550RegMap = 7'h1C;
101
5'd14:  fnRealToLM4550RegMap = 7'h20;
102
5'd15:  fnRealToLM4550RegMap = 7'h22;
103
5'd16:  fnRealToLM4550RegMap = 7'h24;
104
5'd17:  fnRealToLM4550RegMap = 7'h26;
105
5'd18:  fnRealToLM4550RegMap = 7'h28;
106
5'd19:  fnRealToLM4550RegMap = 7'h2A;
107
5'd20:  fnRealToLM4550RegMap = 7'h2C;
108
5'd21:  fnRealToLM4550RegMap = 7'h32;
109
5'd22:  fnRealToLM4550RegMap = 7'h5A;
110
5'd23:  fnRealToLM4550RegMap = 7'h74;
111
5'd24:  fnRealToLM4550RegMap = 7'h7A;
112
5'd25:  fnRealToLM4550RegMap = 7'h7C;
113
5'd26:  fnRealToLM4550RegMap = 7'h7E;
114
// These registers aren't part of the real LM4550
115
// They are provided as a scratchpad space.
116
5'd27:  fnRealToLM4550RegMap = 7'h60;
117
5'd28:  fnRealToLM4550RegMap = 7'h62;
118
5'd29:  fnRealToLM4550RegMap = 7'h64;
119
5'd30:  fnRealToLM4550RegMap = 7'h66;
120
5'd31:  fnRealToLM4550RegMap = 7'h68;
121
default:        fnRealToLM4550RegMap = 7'd00;
122
endcase
123
end
124
endfunction
125
 
126
function [4:0] fnLM4550ToRealRegMap;
127
input [6:0] regno;
128
begin
129
case (regno)
130
7'h00:  fnLM4550ToRealRegMap = 5'd0;
131
7'h02:  fnLM4550ToRealRegMap = 5'd1;
132
7'h04:  fnLM4550ToRealRegMap = 5'd2;
133
7'h06:  fnLM4550ToRealRegMap = 5'd3;
134
7'h0A:  fnLM4550ToRealRegMap = 5'd4;
135
7'h0C:  fnLM4550ToRealRegMap = 5'd5;
136
7'h0E:  fnLM4550ToRealRegMap = 5'd6;
137
7'h10:  fnLM4550ToRealRegMap = 5'd7;
138
7'h12:  fnLM4550ToRealRegMap = 5'd8;
139
7'h14:  fnLM4550ToRealRegMap = 5'd9;
140
7'h16:  fnLM4550ToRealRegMap = 5'd10;
141
7'h18:  fnLM4550ToRealRegMap = 5'd11;
142
7'h1A:  fnLM4550ToRealRegMap = 5'd12;
143
7'h1C:  fnLM4550ToRealRegMap = 5'd13;
144
7'h20:  fnLM4550ToRealRegMap = 5'd14;
145
7'h22:  fnLM4550ToRealRegMap = 5'd15;
146
7'h24:  fnLM4550ToRealRegMap = 5'd16;
147
7'h26:  fnLM4550ToRealRegMap = 5'd17;
148
7'h28:  fnLM4550ToRealRegMap = 5'd18;
149
7'h2A:  fnLM4550ToRealRegMap = 5'd19;
150
7'h2C:  fnLM4550ToRealRegMap = 5'd20;
151
7'h32:  fnLM4550ToRealRegMap = 5'd21;
152
7'h5A:  fnLM4550ToRealRegMap = 5'd22;
153
7'h74:  fnLM4550ToRealRegMap = 5'd23;
154
7'h7A:  fnLM4550ToRealRegMap = 5'd24;
155
7'h7C:  fnLM4550ToRealRegMap = 5'd25;
156
7'h7E:  fnLM4550ToRealRegMap = 5'd26;
157
// These registers aren't part of the real LM4550
158
// They are provided as a scratchpad space.
159
7'h60:  fnLM4550ToRealRegMap = 5'd27;
160
7'h62:  fnLM4550ToRealRegMap = 5'd28;
161
7'h64:  fnLM4550ToRealRegMap = 5'd29;
162
7'h66:  fnLM4550ToRealRegMap = 5'd30;
163
7'h68:  fnLM4550ToRealRegMap = 5'd31;
164
default:        fnLM4550ToRealRegMap = 5'd31;
165
endcase
166
end
167
endfunction
168
 
169
//---------------------------------------------------------------------
170
// Shadow registers
171
//---------------------------------------------------------------------
172
reg [31:0] dirty;                        // Indicates which registers need to be written to the LM4550
173
reg [15:0] regfile [0:31];        // Mimic registers
174
wire [15:0] rfrgno = regfile[rgno];
175
wire [15:0] rfadri = regfile[fnLM4550ToRealRegMap(adr_i[6:0])];
176
 
177
// Shadow register write
178
always @(posedge clk_i)
179
begin
180
        if (cs & we_i) begin
181
                regfile[fnLM4550ToRealRegMap(adr_i[6:0])] <= dat_i;
182
        end
183
end
184
 
185
// Shadow register read
186
// Several of the LM4550 registers are read-only static values.
187
// They are just hard-coded here.
188
always @(posedge clk_i)
189
        if (cs) begin
190
                case (adr_i[6:0])
191
                7'h00:  dat_o <= 16'h0D50;
192
                7'h22:  dat_o <= 16'h0101;
193
                7'h26:  dat_o <= reg26;
194
                7'h5A:  dat_o <= 16'h0000;
195
                7'h68:  dat_o <= {16{|dirty}};
196
                7'h74:  dat_o <= 16'h0000;
197
                7'h7C:  dat_o <= 16'h4E53;
198
                7'h7E:  dat_o <= 16'h4350;
199
                default:        dat_o <= rfadri;
200
                endcase
201
        end
202
        else
203
                dat_o <= 16'h0000;
204
 
205
//---------------------------------------------------------------------
206
// Update to read LM4550 registers
207
//
208
// Only one register at a time may be written to the LM4550. Choose
209
// a register based on which dirty bit is set. The dirty bit is reset
210
// once the register is written to the LM4550.
211
//---------------------------------------------------------------------
212
always @(posedge clk_i)
213
casex(dirty)
214
32'b1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd31;
215
32'b01xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd30;
216
32'b001xxxxxxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd29;
217
32'b0001xxxxxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd28;
218
32'b00001xxxxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd27;
219
32'b000001xxxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd26;
220
32'b0000001xxxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd25;
221
32'b00000001xxxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd24;
222
32'b000000001xxxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd23;
223
32'b0000000001xxxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd22;
224
32'b00000000001xxxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd21;
225
32'b000000000001xxxxxxxxxxxxxxxxxxxx:   rgno <= 5'd20;
226
32'b0000000000001xxxxxxxxxxxxxxxxxxx:   rgno <= 5'd19;
227
32'b00000000000001xxxxxxxxxxxxxxxxxx:   rgno <= 5'd18;
228
32'b000000000000001xxxxxxxxxxxxxxxxx:   rgno <= 5'd17;
229
32'b0000000000000001xxxxxxxxxxxxxxxx:   rgno <= 5'd16;
230
32'b00000000000000001xxxxxxxxxxxxxxx:   rgno <= 5'd15;
231
32'b000000000000000001xxxxxxxxxxxxxx:   rgno <= 5'd14;
232
32'b0000000000000000001xxxxxxxxxxxxx:   rgno <= 5'd13;
233
32'b00000000000000000001xxxxxxxxxxxx:   rgno <= 5'd12;
234
32'b000000000000000000001xxxxxxxxxxx:   rgno <= 5'd11;
235
32'b0000000000000000000001xxxxxxxxxx:   rgno <= 5'd10;
236
32'b00000000000000000000001xxxxxxxxx:   rgno <= 5'd9;
237
32'b000000000000000000000001xxxxxxxx:   rgno <= 5'd8;
238
32'b0000000000000000000000001xxxxxxx:   rgno <= 5'd7;
239
32'b00000000000000000000000001xxxxxx:   rgno <= 5'd6;
240
32'b000000000000000000000000001xxxxx:   rgno <= 5'd5;
241
32'b0000000000000000000000000001xxxx:   rgno <= 5'd4;
242
32'b00000000000000000000000000001xxx:   rgno <= 5'd3;
243
32'b000000000000000000000000000001xx:   rgno <= 5'd2;
244
32'b0000000000000000000000000000001x:   rgno <= 5'd1;
245
32'b00000000000000000000000000000001:   rgno <= 5'd0;
246
default:        rgno <= 5'd0;
247
endcase
248
 
249
// Detect an edge on the SYNC signal to determine when to populate a frame with
250
// data. The AC97_controller streams continuously to the LM4550 at 48kHz frame
251
// rate.
252
edge_det u2 (.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(SYNC), .pe(pe_sync), .ne(), .ee() );
253
wire doRead = fnRealToLM4550RegMap(rgno)==7'h26;
254
 
255
always @(posedge clk_i)
256
if (rst_i) begin
257
        dirty <= 32'd0;
258
        slot0 <= 0;
259
        slot1 <= 0;
260
        slot2 <= 0;
261
        slot3 <= 0;
262
        slot4 <= 0;
263
        slot5 <= 0;
264
        slot6 <= 0;
265
        slot7 <= 0;
266
        slot8 <= 0;
267
        slot9 <= 0;
268
        slot10 <= 0;
269
        slot11 <= 0;
270
        slot12 <= 0;
271
end
272
else begin
273
        if (cs & we_i)
274
                dirty[fnLM4550ToRealRegMap(adr_i[6:0])] <= 1'b1;
275
        if (RESET)      begin // RESET is active low!
276
                if (codecReady & pe_sync & |dirty) begin
277
                        if (rgno < 7'd27) begin
278
                                slot0[15] <= 1'b1;              // frame is valid
279
                                slot0[14] <= 1'b1;              // valid control data
280
                                slot0[13] <= 1'b1;              // control data in slot2
281
                                slot0[12:0] <= 13'd0;
282
                                slot1[19] <= doRead;            // Write, 1= read
283
                                slot1[18:12] <= fnRealToLM4550RegMap(rgno);
284
                                slot1[11:0] <= 12'd0;    // reserved
285
                                slot2[19:4] <= doRead ? 16'h0000 : rfrgno;
286
                                slot2[3:0] <= 4'd0;
287
                                slot3 <= 20'd0;
288
                                slot4 <= 20'd0;
289
                                slot6 <= 20'd0;
290
                                slot7 <= 20'd0;
291
                                slot8 <= 20'd0;
292
                                slot9 <= 20'd0;
293
                                // these slots are always zero
294
                                slot5 <= 20'd0;
295
                                slot10 <= 20'd0;
296
                                slot11 <= 20'd0;
297
                                slot12 <= 20'd0;
298
                                dirty[rgno] <= 1'b0;
299
                        end
300
                        else begin
301
                                dirty[rgno] <= 1'b0;
302
                                slot0[15] <= 1'b1;
303
                                slot0[14] <= 1'b0;
304
                                slot0[13] <= 1'b0;
305
                                slot0[12] <= 1'b1;      // left data in slot3
306
                                slot0[11] <= 1'b1;      // right data in slot4
307
                                slot0[10:0] <= 11'd0;
308
                                slot1 <= 20'd0;
309
                                slot2 <= 20'd0;
310
                                slot3[19:2] <= PSGout;
311
                                slot3[1:0] <= 2'b00;
312
                                slot4[19:2] <= PSGout;
313
                                slot4[1:0] <= 2'b00;
314
                                slot6 <= 20'd0;
315
                                slot7 <= 20'd0;
316
                                slot8 <= 20'd0;
317
                                slot9 <= 20'd0;
318
                                // these slots are always zero
319
                                slot5 <= 20'd0;
320
                                slot10 <= 20'd0;
321
                                slot11 <= 20'd0;
322
                                slot12 <= 20'd0;
323
                        end
324
                end
325
                else if (codecReady & pe_sync) begin
326
                        slot0[15] <= 1'b1;
327
                        slot0[14] <= 1'b0;
328
                        slot0[13] <= 1'b0;
329
                        slot0[12] <= 1'b1;      // left data in slot3
330
                        slot0[11] <= 1'b1;      // right data in slot4
331
                        slot0[10:0] <= 11'd0;
332
                        slot1 <= 20'd0;
333
                        slot2 <= 20'd0;
334
                        slot3[19:2] <= PSGout;
335
                        slot3[1:0] <= 2'b00;
336
                        slot4[19:2] <= PSGout;
337
                        slot4[1:0] <= 2'b00;
338
                        slot6 <= 20'd0;
339
                        slot7 <= 20'd0;
340
                        slot8 <= 20'd0;
341
                        slot9 <= 20'd0;
342
                        // these slots are always zero
343
                        slot5 <= 20'd0;
344
                        slot10 <= 20'd0;
345
                        slot11 <= 20'd0;
346
                        slot12 <= 20'd0;
347
                end
348
                // Send empty frames until the codec is ready.
349
                else if (pe_sync) begin
350
                        slot0 <= 0;
351
                        slot1 <= 0;
352
                        slot2 <= 0;
353
                        slot3 <= 0;
354
                        slot4 <= 0;
355
                        slot5 <= 0;
356
                        slot6 <= 0;
357
                        slot7 <= 0;
358
                        slot8 <= 0;
359
                        slot9 <= 0;
360
                        slot10 <= 0;
361
                        slot11 <= 0;
362
                        slot12 <= 0;
363
                end
364
        end
365
end
366
 
367
always @(posedge clk_i)
368
if (rst_i) begin
369
        reg26 <= 16'h0000;
370
        codecReady <= 1'b0;
371
end
372
else begin
373
        if (RESET) begin        // RESET is active low!
374
                if (pe_sync) begin
375
                        if (slot0i[15:13]==3'b111) begin
376
                                if (slot1i[18:12]==7'h26) begin
377
                                        reg26 <= slot2i[19:4];
378
                                end
379
                        end
380
                        if (slot0i[15]==1'b1) begin
381
                                codecReady <= 1'b1;
382
                        end
383
                end
384
        end
385
        else
386
                codecReady <= 1'b0;
387
end
388
 
389
ac97_controller u1
390
(
391
    .SYSCLK(clk_i),                             // up to 125MHz
392
        .SYSTEM_RESET(rst_i),           // active on 1
393
    .BIT_CLK(BIT_CLK),                  // 12,288 MHz
394
    .SDATA_IN(SDATA_IN),
395
        .SYNC(SYNC),
396
    .SDATA_OUT(SDATA_OUT),
397
    .RESET(RESET),
398
        .DONE(),
399
        .Slot0_in(slot0),
400
        .Slot1_in(slot1),
401
        .Slot2_in(slot2),
402
        .Slot3_in(slot3),
403
        .Slot4_in(slot4),
404
        .Slot5_in(slot5),
405
        .Slot6_in(slot6),
406
        .Slot7_in(slot7),
407
        .Slot8_in(slot8),
408
        .Slot9_in(slot9),
409
        .Slot10_in(slot10),
410
        .Slot11_in(slot11),
411
        .Slot12_in(slot12),
412
        .Slot0_out(slot0i),
413
        .Slot1_out(slot1i),
414
        .Slot2_out(slot2i),
415
        .Slot3_out(),
416
        .Slot4_out(),
417
        // The following slots are not used, and they are always zero
418
        .Slot5_out(),
419
        .Slot6_out(),
420
        .Slot7_out(),
421
        .Slot8_out(),
422
        .Slot9_out(),
423
        .Slot10_out(),
424
        .Slot11_out(),
425
        .Slot12_out()
426
);
427
 
428
 
429
endmodule

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.