Làm cách nào để ghi vào bộ nhớ flash SPI?


8

Tôi đang làm việc trên một ứng dụng âm thanh thay vì lưu trữ dữ liệu âm thanh trên thẻ SD ( Waveshield trên Arduino) Tôi đang lưu trữ nó trên IC bộ nhớ flash SPI và cuộn bảng của riêng tôi với MCU, DAC và amp.

Tôi đang sử dụng Winbond W25Q80BVSSIG .

Tôi khá quen thuộc với việc lập trình AVR bằng cách sử dụng AVRISP mkII hoặc USBTiny , việc ghi dữ liệu vào flash được thực hiện với cùng một lập trình viên? Tôi đã không thể tìm thấy bất cứ điều gì khi tìm kiếm lập trình viên bộ nhớ flash SPI cụ thể.

Câu hỏi này là một theo dõi đến cái này .


Tôi không biết có tồn tại một lập trình viên đặc biệt có thể thực hiện việc này dễ dàng cho bạn hay không, từ PC hoặc thứ gì đó, nhưng nếu có CPLD liên quan đến mạch của bạn, bạn có thể định cấu hình đó để ghi dữ liệu vào bộ nhớ flash.
deed02392

Tôi nghĩ rằng các mô-đun spi flash được thiết kế bây giờ một ngày để lưu trữ firmware / bios mà cpu sử dụng trong hầu hết các máy tính. Không cho thiết bị lưu trữ mạnh mẽ.
soái ca thủ công

Câu trả lời:


12

Nếu bạn chỉ đơn giản là tìm cách lập trình flash Winbond SPI với dữ liệu "được tải sẵn" mà vi điều khiển của bạn sẽ đọc để sử dụng khi nó đang chạy thì điều bạn sẽ muốn xem xét là một lập trình viên có thể lập trình trong mạch của chip SPI Flash. Điều này còn được gọi là lập trình trong hệ thống (ISP).

Một lựa chọn là lập trình viên từ DediProg. Thiết bị được kết nối USB này có thể lập trình theo mạch nếu bạn thiết kế bảng chính xác. Họ thậm chí bán một clip bộ điều hợp có thể gắn vào gói SOW-16 mà không phải thiết kế trong một tiêu đề lập trình riêng trên bảng của bạn. DediProg có các bản tin thông tin ứng dụng có sẵn để giúp thiết kế chính xác cho việc sử dụng mạch. Chiến lược chính cho thiết kế là tìm ra một cách đơn giản để cô lập trình điều khiển giao diện SPI trong hệ thống MCU của bạn để chúng không can thiệp vào các trình điều khiển trong nhóm lập trình SPI. Cách đơn giản nhất để thực hiện việc này là đặt các điện trở nối tiếp vào các đường dẫn hướng MCU giữa MCU và SPI Flash. Lập trình viên sẽ kết nối ở phía flash SPI của các điện trở loạt. Các phương pháp thay thế có thể bao gồm thêm MUX hoặc các công tắc tương tự trong các dòng giao diện được điều khiển. Một kế hoạch thông minh hơn nữa là thêm "

nhập mô tả hình ảnh ở đây

Một lựa chọn thứ hai cũng cần xem xét là lập trình USB từ ASIX . Presto có thể thực hiện nhiều loại thiết bị SPI và I 2 C khác nhau bao gồm cả thiết bị SPI Flash. Tôi có một trong những thiết bị này dành riêng cho lập trình MCU Atmel và các loại thiết bị SPI Flash khác nhau. Đó là một giải pháp hiệu quả chi phí hơn các đơn vị trên nhưng không hoàn toàn linh hoạt. Thiết bị đắt tiền hơn của họ được gọi là Forte có thể làm được nhiều việc hơn vì nó có nhiều chân giao diện mục tiêu hơn.

nhập mô tả hình ảnh ở đây

Đôi khi nó có thể có ích khi có thể kết nối một chương trình với bảng mục tiêu mà không cần phải thêm tiêu đề lập trình. Một giải pháp hay cho việc này là đặt một bộ miếng đệm nhỏ trong một dấu chân đặc biệt được xác định bởi một công ty có tên TagConnect . Họ sản xuất và bán một loạt các cáp lập trình kết nối nhanh có chân pogo có dấu chân đặc biệt trên bảng. Có sẵn các phiên bản cáp 6 pin, 10 pin và 14 pin để phù hợp với một loạt các ứng dụng. Chi phí cáp rất hợp lý.

nhập mô tả hình ảnh ở đây


Điều này rất hữu ích. Tôi đang lên kế hoạch lập trình bộ nhớ flash trước khi hàn nó vào PCB cuối cùng. Cho đến nay tôi đã làm điều này cho MCU và nó hoạt động tốt. Tôi không quyết định liệu việc cung cấp chân ISP trên PCB có phải là ý tưởng tốt hay không, vì chúng không được lập trình lại sau khi hoàn thành.
JYelton

1
@JYelton - Theo kinh nghiệm của tôi, bạn nên lập kế hoạch cho ISP vì nhiều lý do. ECO's (Đơn đặt hàng thay đổi kỹ thuật) là một thực tế của cuộc sống trong chu kỳ sản phẩm. Ai đó hoặc thứ gì đó sẽ yêu cầu thay đổi nội dung Flash khi bạn bắt đầu sản xuất. Flash chip đôi khi dễ bị nhiễu nhiễu bất ngờ trong mạch và cuối cùng có nội dung của chúng bị xâm phạm. Một lý do khác để cung cấp cho ISP.
Michael Karas

1
Có một tiêu đề "tiêu chuẩn" tương thích với hầu hết các lập trình viên này ít nhất là thông qua cáp bộ điều hợp đi kèm? Tôi đã thấy các tiêu đề pin 2x4 và 2x5 với một loạt các pinout khác nhau Cũng xem flashrom.org/Supported_hardware
kert 23/03 '

Mọi người đều có ý tưởng riêng của họ hợp lý. Cáp kết nối thẻ là $ 35 ~ $ 40.
đánh dấu

1
@markrages - Nhưng đối với một phòng thí nghiệm phát triển nhất định hoặc trạm lập trình nhà máy, bạn chỉ cần mua một cáp. Bạn không cần một cho mỗi sản phẩm. Thêm vào đó, các dây cáp này rẻ hơn nhiều khi cố gắng cuộn cố định pogo-pin của riêng bạn để cho phép ISP không có đầu nối.
Michael Karas

8

Tôi cá là bạn có thể làm điều đó với Bus Pirate mà không cần thông qua MCU của mình ... điều đó cho phép bạn thực hiện các tương tác nối tiếp tùy ý trực tiếp đến một con chip bằng giao tiếp SPI, I2C hoặc UART. Nó có thể mất một chút công việc để "kịch bản" nó, nhưng nó sẽ cho phép bạn thực hiện công việc.

Tôi cũng đã thấy các công cụ chuyên dụng để tải EEPROM trực tiếp qua I2C, nhưng không flash và không SPI cụ thể.


Tôi bắt đầu tự hỏi liệu lựa chọn đèn flash SPI của tôi có tốt không, do cách (rõ ràng) che khuất các phương pháp được viết cho những thứ đã nổ.
JYelton

1
Điều đó sẽ mất một số kịch bản nghiêm túc, nhưng dù sao cũng tốt. Một chút để phức tạp cho hương vị của tôi. Có lẽ bạn nên xem xét một thẻ SD? Sau đó, bạn chỉ cần quan tâm đến việc đọc, viết cho nó bằng máy tính
chwi

Nguyên mẫu hiện tại sử dụng Arduino và Waveshield (có đầu đọc thẻ SD). Tôi muốn tránh xa thẻ SD vì tôi tin rằng chi phí sẽ ít hơn (không có đầu đọc và thẻ) và cũng có nhiều bằng chứng giả mạo hơn.
JYelton

7

Tôi chưa bao giờ nghe thấy bất kỳ công cụ nào khác nói trực tiếp SPI với một con chip như vậy và tôi nghĩ rằng điều đó là không thể vì các chip "tất cả" yêu cầu các cuộc gọi khác nhau cho các hoạt động khác nhau.

Chip cần các lệnh gọi SPI để ghi, đọc, thay đổi cung, kích thước dữ liệu, v.v. Trong 7.2 Hướng dẫn chương trong biểu dữ liệu, bạn có thể thấy tất cả các lệnh SPI bạn có thể gửi cho nó. Do đó, vì tất cả các bộ nhớ flash ngoài không có cùng một bộ hướng dẫn, bạn cần viết một ứng dụng tùy chỉnh cho cái này.

EDIT: Để theo dõi, tôi thực sự muốn giới thiệu một trong những bộ nhớ flash SPI của Atmels, vì hầu hết trong số họ đã viết mã mở có sẵn cho họ. Nhìn vào bài đăng này từ AVRFreaks sẽ cung cấp cho bạn mã cho một số chip flash nối tiếp Atmels AT45xxxx.


Nếu tôi hiểu bạn chính xác, tôi có nên viết chương trình cho MCU của mình để sau đó ghi dữ liệu vào bộ nhớ flash không? Vấn đề là MCU có bộ nhớ ít hơn so với đèn flash ngoài, vì vậy tôi hơi bị mất.
JYelton

Đúng. Bạn có thể gửi dữ liệu từ dòng nối tiếp máy tính của mình với UART mà bạn ghi vào flash. Ngoài ra, bạn có thể viết một số chương trình cho MCU, lập trình flash một số khối cùng một lúc. Nó có thể hơi tốn thời gian nhưng nó hoạt động vì đèn flash bên ngoài sẽ không bị xóa miễn là bạn theo dõi thay đổi các lĩnh vực một cách chính xác
chwi

2
Đây là câu trả lời chính xác. Vì vậy, bạn sẽ cần một chương trình trên PC để tải các đoạn xuống MCU, sau đó ghi chúng vào flash. Nó có ích nếu kiểm tra lỗi và bạn không phải viết chương trình mới trên PC; vì vậy tôi khuyên bạn nên tìm một số mã cho XMODEM hoặc tương tự.
pjc50

@ pjc50 ... không quá nhanh khi tuyên bố 'câu trả lời đúng' :)
Abbeyatcu

Trên thực tế nhiều lập trình viên có thể lập trình bộ nhớ flash; và một trong những chương trình lập trình phổ biến được sử dụng với micrô atmel khá gần với SPI để bắt đầu.
Chris Stratton

4

Tôi đã mua một lập trình viên " FlashCAT " từ Máy tính nhúng với giá khoảng 30 đô la Mỹ. Thật dễ dàng để kết nối với PC thông qua USB và ghi các tập tin vào bộ nhớ flash Winbond. Các phương pháp và lập trình viên trong các câu trả lời khác có lẽ cũng tốt như vậy, một số đắt hơn hoặc DIY, nhưng đây là một cách rẻ tiền và đơn giản phù hợp với những gì tôi đang tìm kiếm.

Dưới đây là hình ảnh của thiết lập:

Lập trình với FlashCAT

Lập trình viên FlashCAT ở bên trái, được kết nối với USB. Nó đang chạy phần mềm lập trình SPI (trái ngược với JTAG) và cung cấp năng lượng cho bộ nhớ flash. Công suất được cung cấp có thể lựa chọn (3,3V hoặc 5V) với một nút nhảy.

Tôi có một ổ cắm SOIC to DIP trên bảng điều khiển để dễ dàng lập trình nhiều chip. (Bạn cũng có thể thấy một IC bộ nhớ flash khác đang ngồi trên bảng.)

Phần mềm FlashCAT

Tôi chưa chuyển đổi tệp âm thanh của mình sang định dạng nhị phân phù hợp, nhưng tôi đã viết tệp WAV 211KB vào bộ nhớ chỉ để kiểm tra, như hình trên. Sau đó tôi đọc lại và lưu nó dưới dạng tệp mới, đổi tên thành .wav và nó phát chính xác trên PC.

Bước tiếp theo sẽ là mã hóa tập tin đúng cách, và viết phần mềm AVR để đọc dữ liệu và gửi nó thông qua một bộ xử lý.

Tuyên bố miễn trừ trách nhiệm: Tôi không liên kết với Máy tính nhúng, tôi chỉ là khách hàng chọn thứ gì đó rẻ tiền và đang chia sẻ thông tin về trải nghiệm với sản phẩm.


4

Loại muộn để thảo luận, nhưng đối với bất cứ ai đọc nó sau khi tìm kiếm ....

Một điều tôi không thấy được đề cập, điều cực kỳ quan trọng khi lập trình chip SPI Flash là kiểm soát pin Chip Chọn (CS_). Chân chọn Chip được sử dụng để ngắt các lệnh cho SPI Flash. Cụ thể, việc chuyển đổi từ CS_ cao sang CS_ thấp phải ngay lập tức trước khi phát hành bất kỳ mã op hoạt động ghi (WREN, BE, SE, PP). Nếu có hoạt động giữa quá trình chuyển đổi CS_ (tức là sau khi CS_ xuống thấp) và trước khi mã op được truyền, mã op thường sẽ bị bỏ qua.

Ngoài ra, điều không thường được giải thích trong bảng dữ liệu SPI Flash, bởi vì đó là một phần vốn có của giao thức SPI, điều rất quan trọng, là với mỗi byte truyền trên bus SPI, người ta sẽ nhận được một byte. Ngoài ra, người ta không thể nhận bất kỳ byte nào, trừ khi người ta truyền một byte.

Thông thường, SPI Master mà người dùng đang chỉ huy, có Bộ đệm truyền, gửi byte ra trên dòng MOSI của bus SPI và Bộ đệm nhận, nhận byte từ dòng MISO của bus SPI.

Để bất kỳ dữ liệu nào xuất hiện trong bộ đệm Nhận, một số dữ liệu phải được gửi ra Bộ đệm truyền. Tương tự, bất cứ khi nào người ta gửi dữ liệu ra khỏi bộ đệm Truyền, dữ liệu sẽ xuất hiện trong Bộ đệm nhận.

Nếu một người không cẩn thận về việc cân bằng việc ghi và ghi đọc, người ta sẽ không biết những gì mong đợi trong bộ đệm Nhận. Nếu tràn bộ đệm Nhận, dữ liệu thường chỉ bị đổ và mất.

Vì vậy, khi một người gửi lệnh đọc, đó là mã op một byte và ba byte địa chỉ, trước tiên người ta sẽ nhận được bốn byte "rác" trong bộ đệm Nhận SPI Master. Bốn byte rác này tương ứng với mã op và ba byte địa chỉ. Trong khi những thứ đó đang được truyền đi, Flash vẫn chưa biết Đọc gì, vì vậy nó chỉ trả về bốn từ rác.

Sau khi bốn từ rác đó được trả lại, để có được bất cứ thứ gì khác trong Bộ đệm nhận, bạn phải truyền một lượng dữ liệu bằng với số lượng bạn muốn đọc. Sau mã op và địa chỉ, không có vấn đề gì bạn truyền tải, nó chỉ là phụ để đẩy Read DAta từ SPI Flash sang Bộ đệm nhận.

Nếu bạn không theo dõi cẩn thận bốn từ rác được trả lại đầu tiên đó, bạn có thể nghĩ rằng một hoặc nhiều trong số chúng là một phần của Dữ liệu Đọc được trả về của bạn.

Vì vậy, để biết bạn thực sự nhận được gì từ bộ đệm nhận, điều quan trọng là phải biết kích thước bộ đệm của bạn, biết cách để biết nó trống hay đầy (thường đăng ký bit trạng thái để báo cáo điều này) và theo dõi cách nhiều thứ bạn đã truyền đi và bạn đã nhận được bao nhiêu.

Trước khi bắt đầu bất kỳ hoạt động SPI Flash nào, bạn nên "rút cạn" Nhận FIFO. Điều này có nghĩa là kiểm tra trạng thái của bộ đệm nhận và làm trống nó (thường được thực hiện bằng cách thực hiện 'đọc' Bộ đệm nhận) nếu nó chưa trống. Thông thường, làm trống (đọc) một Bộ đệm Nhận đã trống không có hại.

Thông tin sau đây có sẵn từ các sơ đồ thời gian trong bảng dữ liệu của SPI Flashes, nhưng đôi khi mọi người bỏ qua các bit. Tất cả các lệnh và dữ liệu được cấp cho flash SPI bằng bus SPI. Trình tự để đọc SPI Flash là:

1) Start with CS_ high.
2) Bring CS_ low.
3) Issue "Read" op code to SPI Flash.
4) Issue three address bytes to SPI Flash.
5) "Receive" four garbage words in Receive Buffer.
6) Transmit as many arbitrary bytes (don't cares) as you wish to receive. 
Number of transmitted bytes after address equals size of desired read.
7) Receive read data in the Receive Buffer.
8) When you've read the desired amount of data, set CS_ high to end the Read command.
If you skip this step, any additional transmissions will be interpreted as 
request for more data from (a continuation of) this Read.

Lưu ý rằng các bước 6 và 7 phải được xen kẽ và lặp lại tùy thuộc vào kích thước đọc và kích thước của Bộ đệm Nhận và Truyền của bạn. Nếu bạn truyền một số lượng từ lớn hơn cùng một lúc, hơn Bộ đệm nhận của bạn có thể giữ, bạn sẽ làm đổ một số dữ liệu.

Để tạo trước một Chương trình Trang hoặc lệnh Viết, hãy thực hiện các bước này. Kích thước trang (thường là 256 byte) và Kích thước ngành (thường là 64K) và các ranh giới liên quan là các thuộc tính của SPI Flash bạn đang sử dụng. Thông tin này phải có trong biểu dữ liệu cho Flash. Tôi sẽ bỏ qua các chi tiết về việc cân bằng bộ đệm Truyền và Nhận.

1) Start with CS_ high.
2) Change CS_ to low.
3) Transmit the Write Enable (WREN) op code.
4) Switch CS_ to high for at least one SPI Bus clock cycle.  This may be tens or
hundreds of host clock cycles.  All write operations do not start until CS_ goes high.  
The preceding two notes apply to all the following 'CS_ to high' steps.
5) Switch CS_ to low.
6) Gadfly loop:   Transmit the 'Read from Status Register' (RDSR) op code and 
one more byte.   Receive two bytes.  First byte is garbage.  Second byte is status.
Check status byte.  If 'Write in Progress' (WIP) bit is set, repeat loop. 
(NOTE:  May also check 'Write Enable Latch' bit is set (WEL) after WIP is clear.)
7) Switch CS_ to high.
8) Switch CS_ to low.
9) Transmit Sector Erase (SE) or Bulk Erase (BE) op code.  If sending SE, then follow
it with three byte address.
10) Switch CS_ to high.
11) Switch CS_ to low.
12) Gadfly loop:  Spin on WIP in Status Register as above in step 6.  WEL will
be unset at end.
13) Switch CS_ to high.
14) Switch CS_ to low.
15) Transmit Write Enable op code (again).
16) Switch CS_ to high.
17) Switch CS_ to low.
18) Gadfly loop:  Wait on WIP bit in Status Register to clear. (WEL will be set.)
19) Transmit Page Program (PP = Write) op code followed by three address bytes.
20) Transmit up to Page Size (typically 256 bytes) of data to write.  (You may allow
Receive data to simply spill over during this operation, unless your host hardware
has a problem with that.)
21) Switch CS_ to high.  
22) SWitch CS_ to low.
23) Gadfly loop:  Spin on WIP in the Status Register.
24) Drain Receive FIFO so that it's ready for the next user.
25)  Optional:  Repeat steps 13 to 24 as needed to write additional pages or
page segments.

Cuối cùng, nếu địa chỉ ghi của bạn không nằm trên ranh giới trang (thường là bội số 256 byte) và bạn viết đủ dữ liệu để vượt qua ranh giới trang sau, dữ liệu sẽ vượt qua ranh giới sẽ được ghi vào đầu trang trong đó địa chỉ chương trình của bạn rơi. Vì vậy, nếu bạn cố gắng viết ba byte đến địa chỉ 0x0FE. Hai byte đầu tiên sẽ được ghi vào 0x0fe và 0x0ff. Byte thứ ba sẽ được ghi vào địa chỉ 0x000.

Nếu bạn truyền một số byte dữ liệu lớn hơn kích thước trang, các byte Earlies sẽ bị loại bỏ và chỉ các byte 256 (hoặc kích thước trang) cuối cùng sẽ được sử dụng để lập trình trang.

Như mọi khi, không chịu trách nhiệm về hậu quả của bất kỳ lỗi, lỗi chính tả, lỗi quá mức hoặc loạn trí ở trên, cũng như trong cách bạn đưa nó vào sử dụng.


"Trước khi bắt đầu bất kỳ thao tác SPI Flash nào, bạn nên" rút "Bộ nhận Nhận. Điều này có nghĩa là kiểm tra trạng thái của bộ đệm nhận và làm trống nó (thường được thực hiện bằng cách thực hiện 'đọc' Bộ đệm Nhận) nếu không đã trống. Thông thường, làm trống (đọc) một Bộ đệm Nhận đã trống không có hại. " Những bước tôi cần thực hiện để đọc trạng thái của bộ đệm nhận và làm trống bộ đệm nhận?

3

Trái ngược với một số tuyên bố ở đây, trong khi có một số SPI Promir kỳ quặc ngoài kia, cũng có một số hướng dẫn tiêu chuẩn được sử dụng bởi rất nhiều Prom SPI, bao gồm cả những cái bạn đã chọn.

Như Abbeyatcu đã đề cập, có những loại cáp 'bit-bash' tốt có thể trực tiếp lập trình SPI. Tín hiệu khôn ngoan, SPI trông rất giống JTAG, do đó, bất kỳ loại cáp bash bit nào cũng có thể được sử dụng cung cấp giao diện là nguồn mở. Giao thức bên trong của flash khá đơn giản.

Chúng tôi sử dụng người anh lớn của bộ phận bạn đang xem để khởi động các bo mạch đồ họa của chúng tôi (256M - 2G). Địa chỉ có thêm một byte để xử lý dung lượng lưu trữ, nhưng nếu không thì các lệnh về cơ bản là giống hệt nhau.

Loại PROM bạn đang sử dụng phải được xóa theo ngành, sau đó được lập trình theo trang. Đọc nhanh hơn đáng kể so với viết (trong trường hợp chúng ta sử dụng, lập trình có thể mất nửa giờ, nhưng đọc toàn bộ PROM mất dưới một giây ở 108 MHz).

Bây giờ cho các lệnh: Có nhiều cách có sẵn các lệnh trong các thiết bị này hơn là thực sự cần thiết để lập trình chúng. Bạn thực sự chỉ cần những điều sau đây:

  • RDID (đọc ID) - chỉ để xác minh PROM và báo hiệu trước khi bạn làm bất cứ điều gì phức tạp hơn.
  • WREN (write enable) - cần thiết trước mỗi lần viết.
  • PP (0x02 - chương trình trang) - cần thiết để lập trình một trang.
  • SE (0x20 - xóa khu vực) - trả lại các bit trong sector thành '1'.
  • RDSR (0x05 - thanh ghi trạng thái đọc) - cần thiết để theo dõi chu kỳ xóa / ghi.
  • FREAD (0x0B - đọc nhanh) - đọc dữ liệu PROM và xác minh ghi.

Nếu bạn muốn biết thêm thông tin, hãy xem ghi chú trả lời về lập trình SPI cho Xilinx FPGA trên trang web của họ (http://www.xilinx.com). Họ thực hiện một tập hợp con các lệnh giảm để các GPU của họ có thể khởi động từ các thiết bị này.

Tôi đã thiết kế lập trình viên của riêng mình để làm điều này dựa trên những gì tôi có sẵn và đã viết một kịch bản lập trình bằng Python, nhưng bạn có thể làm tương tự bằng cách sử dụng cáp. Trong trường hợp của bạn, tôi sẽ nghiêm túc xem xét việc làm mọi thứ gián tiếp thông qua MCU như Michael Karas gợi ý. Bạn không cần phải lập trình toàn bộ PROM từ MCU trong một lần - bạn có thể thực hiện theo lĩnh vực.


2

Bạn có thể định hướng lại USBtiny để lập trình bộ nhớ flash thay vì MCU mục tiêu nếu bạn cảm thấy thoải mái khi thay đổi chương trình. Tuy nhiên, có thể không có đủ bộ nhớ để làm cho nó đủ linh hoạt để lập trình cả MCU và đèn flash.

Ở đâu đó tôi có một bảng từ một dự án có cả đèn flash ATTINY và SPI, và sử dụng như một Arduino như một "lập trình viên" có sẵn. Một sửa đổi nhỏ của bản phác thảo ISP được sử dụng để lập trình MCU với avrdude, sau đó một tiện ích tùy chỉnh sẽ gửi một chuỗi đặt bản phác thảo ở chế độ đặc biệt và ghi các khối dữ liệu vào flash SPI.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.